I'm using AutoFixture, Moq, and XUnit extensions ([Theory]
attribute) as described in this blog post http://blog.ploeh.dk/2010/10/08/AutoDataTheorieswithAutoFixture.
I've noticed that most of unit tests look like this:
[Theory, AutoMoqData]
public void Test(
[Frozen] Mock<IServiceOne> serviceOne,
[Frozen] Mock<IServiceTwo> serviceTwo,
MyClass classUnderTest)
{
// Arrange
serviceOne
.Setup(m => m.Get(It.IsAny<int>()));
serviceTwo
.Setup(m => m.Delete(It.IsAny<int>()));
// MyClass has a constructor with arguments for IServiceOne, and IServiceTwo
// classUnderTest will use the two mocks specified above
// Act
var result = classUnderTest.Foo();
// Assert
Assert.True(result);
}
As opposed to always decorating the mocks with [Frozen]
, is there a way to setup the fixture to always freeze mocks?
Here's the AutoMoqData
attribute:
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute()
: base(new Fixture().Customize(new AutoMoqCustomization()))
{
}
}
Although it's currently not built-in, it's easy to write a general purpose Decorator that freezes objects as they leave the AutoFixture Tree of Responsibility:
public class MemoizingBuilder : ISpecimenBuilder
{
private readonly ISpecimenBuilder builder;
private readonly ConcurrentDictionary<object, object> instances;
public MemoizingBuilder(ISpecimenBuilder builder)
{
this.builder = builder;
this.instances = new ConcurrentDictionary<object, object>();
}
public object Create(object request, ISpecimenContext context)
{
return this.instances.GetOrAdd(
request,
r => this.builder.Create(r, context));
}
}
Notice that it Decorates another ISpecimenBuilder
, but remembers all values before it returns them. If the same request arrives again, it'll return the memoized value.
While you can't extend AutoMoqCustomization
, you can replicate what it does (it's only two lines of code), and use the MemoizingBuilder
around it:
public class AutoFreezeMoq : ICustomization
{
public void Customize(IFixture fixture)
{
if (fixture == null)
throw new ArgumentNullException("fixture");
fixture.Customizations.Add(
new MemoizingBuilder(
new MockPostprocessor(
new MethodInvoker(
new MockConstructorQuery()))));
fixture.ResidueCollectors.Add(new MockRelay());
}
}
Use this AutoFreezeMoq
instead of AutoMoqCustomization
. It will freeze all mocks, and all interfaces and abstract base classes created from those mocks.
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute()
: base(new Fixture().Customize(new AutoFreezeMoq()))
{
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With