AutoFixture: mock methods don't return a frozen instance
Asked Answered
C

2

7

I'm trying to write this simple test:

var fixture = new Fixture().Customize(new AutoMoqCustomization());

var postProcessingAction = fixture.Freeze<Mock<IPostProcessingAction>>();
var postProcessor = fixture.Freeze<PostProcessor>();

postProcessor.Process("", "");

postProcessingAction.Verify(action => action.Do());

The Verify check fails.
The code for postProcessor.Process is

public void Process(string resultFilePath, string jobId)
{
    IPostProcessingAction postProcessingAction =
        postProcessingActionReader
            .CreatePostProcessingActionFromJobResultXml(resultFilePath);

    postProcessingAction.Do();
}

postProcessingActionReader is an interface field initialized through the constructor.

I'm expecting the test to pass but it fails, it turns out the instance of IPostProessingAction returned from the CreatePostProcessingActionFromJobResultXml method is not the same instance as returned from fixture.Freeze<>.

My expectation was that after freezing this Mock object it would inject the underlying mock of the IPostProcessingAction interface in every place its required as well as make all mock methods returning IPostProcessingAction return this same object.

Is my expectation about the return value of the mock methods incorrect? Is there a way to change this behavior so that mock methods return the same frozen instance?

Canteen answered 5/2, 2013 at 13:47 Comment(1)
How does the postProcessingActionReader implementation looks like?Incunabulum
I
5

You need to Freeze the IPostProcessingActionReader component.

The following test will pass:

[Fact]
public void Test()
{
    var fixture = new Fixture()
        .Customize(new AutoMoqCustomization());

    var postProcessingActionMock = new Mock<IPostProcessingAction>();

    var postProcessingActionReaderMock = fixture
        .Freeze<Mock<IPostProcessingActionReader>>();

    postProcessingActionReaderMock
        .Setup(x => x.CreatePostProcessingActionFromJobResultXml(
            It.IsAny<string>()))
        .Returns(postProcessingActionMock.Object);

    var postProcessor = fixture.CreateAnonymous<PostProcessor>();
    postProcessor.Process("", "");

    postProcessingActionMock.Verify(action => action.Do());
}

Assuming that the types are defined as:

public interface IPostProcessingAction
{
    void Do();
}

public class PostProcessor
{
    private readonly IPostProcessingActionReader actionReader;

    public PostProcessor(IPostProcessingActionReader actionReader)
    {
        if (actionReader == null)
            throw new ArgumentNullException("actionReader");

        this.actionReader = actionReader;
    }

    public void Process(string resultFilePath, string jobId)
    {
        IPostProcessingAction postProcessingAction = this.actionReader
            .CreatePostProcessingActionFromJobResultXml(resultFilePath);

        postProcessingAction.Do();
    }
}

public interface IPostProcessingActionReader
{
    IPostProcessingAction CreatePostProcessingActionFromJobResultXml(
        string resultFilePath);
}

In case you use AutoFixture declaratively with the xUnit.net extension the test could be simplified even further:

[Theory, AutoMoqData]
public void Test(
    [Frozen]Mock<IPostProcessingActionReader> readerMock,
    Mock<IPostProcessingAction> postProcessingActionMock,
    PostProcessor postProcessor)
{
    readerMock
        .Setup(x => x.CreatePostProcessingActionFromJobResultXml(
            It.IsAny<string>()))
        .Returns(postProcessingActionMock.Object);

    postProcessor.Process("", "");

    postProcessingActionMock.Verify(action => action.Do());
}

The AutoMoqDataAttribute is defined as:

internal class AutoMoqDataAttribute : AutoDataAttribute
{
    internal AutoMoqDataAttribute()
        : base(new Fixture().Customize(new AutoMoqCustomization()))
    {
    }
}
Incunabulum answered 5/2, 2013 at 14:26 Comment(2)
Thank you for the thorough answer! I wonder if there's a library like AutoFixture which would go further and set up method return results on mocks automatically so I don't have to do the extra work of return value setup.Canteen
You are welcome! Currently, you can try to automatically fill the properties of auto-mocked instances with this answer.Incunabulum
B
5

As of 3.20.0, you can use AutoConfiguredMoqCustomization. This will automatically configure all mocks so that their members' return values are generated by AutoFixture.

In other words, it will auto-configure your postProcessingActionReader to return the frozen postProcessingAction.

Just change this:

var fixture = new Fixture().Customize(new AutoMoqCustomization());

to this:

var fixture = new Fixture().Customize(new AutoConfiguredMoqCustomization());
Brother answered 21/8, 2014 at 18:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.