mockingmoqautofixtureautomoq

AutoFixture AutoMoq - SetReturnsDefault() does not work with fixture created mocks


I have a mock. This mock has two methods, MethodA() and MethodB(). I want to setup both methods to return false. I created various versions of the code, all of them should work, but some don't:

These work:

1.

var mock = fixture.Freeze<Mock<MyInterface>>();
mock
    .Setup(m => m.MethodA(It.IsAny<T>(), It.IsAny<T>()))
    .ReturnsAsync(false);
mock
    .Setup(m => m.MethodB(It.IsAny<T>(), It.IsAny<T>()))
    .ReturnsAsync(false);

var sut = fixture.Create<MySut>();
sut.Do(); // Calls MethodA() and MethodB() - Both return false, works

2.

var mock = new Mock<MyInterface>();
mock.SetReturnsDefault(Task.FromResult(false));
fixture.Inject(mock);

var sut = fixture.Create<MySut>();
sut.Do(); // Calls MethodA() and MethodB() - Both return false, works

3.

var mock = new Mock<MyInterface>();
mock.SetReturnsDefault(Task.FromResult(false));
fixture.Inject(mock.Object);

var sut = fixture.Create<MySut>();
sut.Do(); // Calls MethodA() and MethodB() - Both return false, works

These don't:

4.

var mock = fixture.Freeze<Mock<MyInterface>>();
mock.SetReturnsDefault(Task.FromResult(false));

var sut = fixture.Create<MySut>();
sut.Do(); // Calls MethodA() and MethodB() - MethodA returns true, fails

5.

var mock = fixture.Create<Mock<MyInterface>>();
mock.SetReturnsDefault(Task.FromResult(false));
fixture.Inject(mock);

var sut = fixture.Create<MySut>();
sut.Do(); // Calls MethodA() and MethodB() - MethodA returns true, fails

6.

var mock = fixture.Create<Mock<MyInterface>>();
mock.SetReturnsDefault(Task.FromResult(false));
fixture.Inject(mock.Object);

var sut = fixture.Create<MySut>();
sut.Do(); // Calls MethodA() and MethodB() - MethodA returns true, fails

Based on results, it seems the culprit is the Fixture.Create() method*. For some reason, if the mock is created using fixture.Create() instead of new keyword, it will not keep the configuration I set up using SetReturnsDefault(), even if the mock is frozen (meaning Fixture.Inject() was called on it). Can someone explain why?


Footnote:

* Fixture.Create() is also called internally when you call Fixture.Freeze() - Freeze is just a shorthand for a call to Fixture.Create() followed by Fixture.Inject()

Therefore, these two snippets are equivalent:

var mock = fixture.Freeze<Mock<MyInterface>>();

-

var mock = fixture.Create<Mock<MyInterface>>();
fixture.Inject(mock);

Solution

  • Since you shared that you're using the AutoMoqCustomization, it explains the behavior you see, though it's due to some internals in both Moq and AutoFixture interacting in a way you may not expect.

    For completeness sake, here's a stubbed out version of the class and interface you mentioned in your question:

    public interface IMyInterface
    {
        Task<bool> MethodA();
    
        Task<bool> MethodB();
    }
    
    public class Sut
    {
        private readonly IMyInterface dep;
    
        public Sut(IMyInterface dep)
        {
            this.dep = dep;
        }
    
        public async Task<bool> Do()
        {
            var one = await dep.MethodA();
            var two = await dep.MethodB();
    
            return one || two;
        }
    }
    

    And here's a test we can use to illustrate the interaction of the two behaviors:

    [Test]
    public async Task FixtureNoConfigureMembers()
    {
        var fixture = new Fixture().Customize(new AutoMoqCustomization() { ConfigureMembers = false });
    
        var mock = fixture.Freeze<Mock<IMyInterface>>();
    
        mock.SetReturnsDefault(Task.FromResult(false));
    
        var sut = fixture.Create<Sut>();
        var result = await sut.Do();
        Assert.False(result);
    }
    

    The two salient implementation details to consider are:

    As both projects are open source, it's simple enough to peek under the covers. I won't paste each individual class here, but you can check Autofixture's source and Moq's source if you desire more detail.

    Moq's SetReturnsDefault has fairly straightforward behavior:

    On the autofixture side...

    AutoMoqCustomization makes it so that, when a specimen of type Mock<T> is requested, AutoFixture will first create the Mock by essentially calling new Mock<T>() (It's slightly more nuanced than that, but it's essentially the case for this interface.)

    After creating the mock, if ConfigureMembers is true, Autofixture will additionally enumerate all the virtual methods of the mocked type, and do the equivalent of

    mock.Setup(m => m.MethodName(It.IsAny<T>... for all arguments... ))
        .Returns(fixture.Create<TReturn>())
    

    This overrides the default behavior of the AutoMoqCustomization, which is to allow Moq to handle the default value selection (usually by having it create its own mocks).

    You can probably see where this is headed. Because AutoFixture has created setups for all of the virtual methods, this completely overrides the SetReturnsDefault behavior provided by Moq.

    Note that this behavior is because Moq didn't used to have the SetReturnsDefault method-- so AutoFixture couldn't utilize the DefaultValueProvider in order to inject specimens that it creates. There's a PR on AutoFixture to change the behavior to utilize the DefaultValueProvider property provided by Moq. Presumably that would restore the ability for SetReturnsDefault to override the behavior for a particular type. (Though I haven't explored the PR in any depth at all)