Why does AutoFixture.AutoMoq make recursive mocks by default?
Asked Answered
B

1

6

Moq does not make recursive mocks by default. That is, for members without expectations on a mock, Moq returns default values. For example, given:

public interface IFoo
{
    Bar Bar();
}

and

public class Bar
{
}

then:

[TestMethod]
public void RecursiveMocksAreDisabledByDefaultInMoq()
{
    var foo = new Mock<IFoo>().Object;
    Assert.IsNull(foo.Bar());
}

However, in AutoFixture.AutoMoq, recursive mocks are enabled by default, as in:

[TestMethod]
public void RecursiveMocksAreEnabledByDefaultInAutoFixture()
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    var foo = fixture.Create<IFoo>();
    Assert.IsNotNull(foo.Bar());
}

Why is that? And, how to turn off automatic recursive mocks in AutoFixture.AutoMoq?

Thanks

Moq.3.1.416.3
AutoFixture.AutoMoq.3.16.5
Bromoform answered 20/2, 2014 at 23:13 Comment(8)
You might find elements of this discussion pertaining to the default return value in AutoFoq useful - AutoFoq doesn't OOTB change Foq behaviour (resulting in null return values from methods OOTB). I also strongly recommend AutoFoq and Foq - however I appreciate changing mocking libs isnt something you 'just do'. I personally for the longest time didnt understand how a mocking library could be so significantly more usable than Moq and discounted it for a long time despite being aware of it. (NB I write most of my tests in F#)Conradconrade
Because "AutoFixture is an opinionated library, and one of the opinions it holds is that nulls are invalid return values." https://mcmap.net/q/1301655/-why-does-autofixture-w-automoqcustomization-stop-complaining-about-lack-of-parameterless-constructor-when-class-is-sealedIndissoluble
See also autofixture.codeplex.com/workitem/4261Indissoluble
@MarkSeemann am aware of the cited post. AutoFoq defaults to returning nulls. AutoMoq returns recursive Mocks. There is an inconsistency. That's my main point. As you know I have no problem with Opinionated design (esp when you explain things which you normally do fantastically) and love, appreciate and use Auto* very much on a daily basisConradconrade
Yes... Until the next major release, where AutoFoq will use Foq's new return strategy, it currently uses Foq's 1.x behaviour (returning null for properties and methods that have not been explicitly setup).Hyetal
@NikosBaxevanis Ah, cool. Wasnt aware that had been agreed (or if I was I forgot :). I personally am happy with things falling that way. Beluchin: I hope you agree this makes sense.Conradconrade
@MarkSeemann Very sorry, for some reason I skipped over you stating that the opinion was that nulls are invalid return values and hence replied as if you hadn't made that clear. Changing the AutoFoq strategy to have Foq also source values for non-mocks from AF closes that loop excellently. (Now I just have to hope you start dogfooding AutoFoq and run into a clash between it's and your meanings of verify :P)Conradconrade
I am ok with the approach of returning recursive mocks by default. It would be nice, though, to have an easy way to disable it.Bromoform
I
3

The comments to the question ought to answer the original question of why, but then there's the follow-up comment:

It would be nice, though, to have an easy way to disable [recursive mocks].

It's not that hard to do. If you look at the implementation of AutoMoqCustomization, it's the use of MockPostProcessor that turns on recursive mocks. If you don't want that, you can create your own Customization that doesn't do that:

public class AutoNonRecursiveMoqCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        if (fixture == null)
            throw new ArgumentNullException("fixture");

        fixture.Customizations.Add(
            new MethodInvoker(
                new MockConstructorQuery()));
        fixture.ResidueCollectors.Add(new MockRelay());
    }
}

MockPostprocessor also sets CallBase to true, so by omitting MockPostprocessor you also disable that CallBase setting.

Indissoluble answered 24/10, 2015 at 9:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.