I'm using NSubstitute to mock my dependencies in my SuT. What I want to do is providing multiple return-values for a specific function. So each time the function is called, it should return a different value. As of the docs I can just add the values comma-seperated as follows:
var row = Substitute.For<ITable>();
table.NextRow().Returns(returnThis, returnThis2, returnThis3)
This function is called in a loop in my SuT:
while((table.NextRow() != null)
{ ... }
Now I'd expect that loop to run exactly as many times as I provide args to the mock - so in the above case exactly three times. When it's invoked a fourth time, I'd expect the mock to return the default-value for whatever the return-type of NextRow
is - so null
in case of a class
. In my actual case I don't have exactly three variables but just an array of values my function should return:
var myArray = new[] { returnThis, returnThis2, returnThis3 };
table.NextRow().Returns(myArray[0], myArray.Skip(1).ToArray());
When I run my SuT, the loop never terminates and eventually I get an OutOfMemoryException
. NextRow
will never return null
, but just keeps returning fake-instances for the ITable
-interface. How can I "stop" providing values?
As you recognized the mock will keep returning fakes when you don't provide further ones. The fakes being returned are those of the last argument to Returns
. So in the following code the variables b
and c
will be equal to two:
[Test]
public void T()
{
var m = Substitute.For<IMyInterface>();
m.MyInt.Returns(1, 2);
var a = m.MyInt;
var b = m.MyInt;
var c = m.MyInt;
}
So afterall you need to explictely add a null
-element into your array:
var rows = myArray.ToList();
rows.Add(null);
table.NextRow.Returns(rows[0], rows.Skip(1).ToArray());
From a pure .Net-centric view it may sound a bit strange that the default-value of a fake isn't the default-value of that type. However it makes sense, when you look at how NSubsitite is used in a fluent way:
var m = Substitute.For<IMyInterface>();
m.MyMember.AnotherMember.Returns(whatever);
When NSubsitite chosed to return null
for reference-types, you'd need to add a further Returns
-call to m.MyMember
as well which will make our test-code pretty cluttered:
var m = Substitute.For<IMyInterface>();
var a = Substitute.For<IAnotherInterface>();
a.AnotherMember.Returns(whatever);
m.MyMember.Returns(a);
However NSubsitite will just give you a default implementation for that members type, when you don't explictely provide one.