Reputation: 15071
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?
Upvotes: 3
Views: 2564
Reputation: 247018
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);
//...
Upvotes: 3