Question

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()))
    {
    }
}
Was it helpful?

Solution

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()))
    {
    }
}

OTHER TIPS

I ended up copying code from the AutoDataAttribute class and modifying it to include a FreezingCustomization.

This is the resulting attribute.

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute()
        : base(new Fixture().Customize(new AutoMoqCustomization()))
    {
    }

    public override IEnumerable<object[]> GetData(System.Reflection.MethodInfo methodUnderTest, Type[] parameterTypes)
    {
        var specimens = new List<object>();
        foreach (var p in methodUnderTest.GetParameters())
        {
            CustomizeFixture(p);
            if (p.ParameterType.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IMock<>)))
            {
                var freeze = new FreezingCustomization(p.ParameterType, p.ParameterType);
                this.Fixture.Customize(freeze);
            }
            var specimen = Resolve(p);
            specimens.Add(specimen);
        }

        return new[] { specimens.ToArray() };
    }

    private void CustomizeFixture(ParameterInfo p)
    {
        var dummy = false;
        var customizeAttributes = p.GetCustomAttributes(typeof(CustomizeAttribute), dummy).OfType<CustomizeAttribute>();
        foreach (var ca in customizeAttributes)
        {
            var c = ca.GetCustomization(p);
            this.Fixture.Customize(c);
        }
    }

    private object Resolve(ParameterInfo p)
    {
        var context = new SpecimenContext(this.Fixture);
        return context.Resolve(p);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top