Search code examples
c#unit-testingmoqautofixtureautomoq

Creating a Mock For a Deeply Nested Class Using AutoFixture/AutoMoq?


I'd like to write a unit test that overrides some readonly property that is fairly deep in an object graph. What I mean is a method like this:

public string MethodToTest(IClassA classA)
{
    return classA.ClassB.ClassC.ClassD.Items[0].Name;
}

Where each ClassN implements an interface IClassN and each property is readonly. So an example interface would be:

IClassA

public interface IClassA { IClassB ClassB { get; } }

And the implementation would look like:

ClassA

public class ClassA : IClassA
{
    public ClassA() { ClassB = new ClassB(); }
    public IClassB ClassB { get; }
}

I want to override the value returned by classA.ClassB.ClassC.ClassD.Items[0].Name, with as little effort as possible. I could create a Mock and have a .Setup to return an IClassB and go up the entire chain just using Moq. But I'd like to avoid that if possible.

I've tried a lot of different things, but have had no luck.

Attempt #1

I thought I could create the chain using fixture.Build()

var moqItem = new Mock<IItem>();
moqItem.Setup(item => item.Name).Returns("My expected value");
var fakeClassD = fixture.Build<IClassD>()
                        .With(d => d.Items, new[] { moqItem.Object });

Obviously I've left off some layers, but it doesn't matter. This fails because the properties are readonly.

Attempt #2

Next I thought I could 'freeze' a particular instance, and whenever the fixture was creating an object, if it saw something of that type, it would use it. I thought I was following the example given here: https://blog.ploeh.dk/2010/03/17/AutoFixtureFreeze/

It shows this like of code: var expectedName = fixture.Freeze("Name");

Based on that, I tried to do something like this:

var moqItem = new Mock<IItem>();
moqItem.Setup(x => x.Name).Returns("My expected value");
fixture.Freeze<IItem[]>(new IItem[] { moqItem.Object });

Sadly, that won't even compile. The Freeze method is expecting a Func of some Composer class of type IItem[] and I haven't been able to figure out how to do that. If I remove the type, similar to the sample code I get

fixture.Freeze(new IItem[] { moqItem.Object });

Which also fails to compile.

Attempt #3

var moqItem = new Mock<IItem>();
moqItem.Setup(x => x.Name).Returns("My expected value");
fixture.Inject<IItem[]>(new IItem[] { moqItem.Object });

Very similar to attempt #2 - only this does compile. I thought that whenever the fixture needed an IItem[] array, it would use the one I setup. But when I call

var attempt3 = fixture.Create<IClassA>();

The behavior isn't what I hoped. attempt3.ClassB.ClassC.ClassD.Items does not contain my mocked item.

TL;DR - How can I override the value returned from Item[0].Name with the least amount of code/effort?


Solution

  • With default Moq out of the box the same can be done with one setup like

    //Arrange
    var expected = "My expected value";
    var mockA = new Mock<IClassA>();
    // auto-mocking hierarchies (a.k.a. recursive mocks)
    mockA.Setup(_ => _.ClassB.ClassC.ClassD.Items[0].Name)
        .Returns(expected);
    
    //...
    
    //Act
    var actual = subject.MethodToTest(mockA.Object);
    
    //...