I’ve had this issue a few times recently. Each time I have it, after I’ve worked out what it was, it makes sense, but I keep running into it. The resulting frustration is this post - that way, it’ll come up the next time I search for it on t’internet.
The Error
The error is as follows:
“NSubstitute.Exceptions.CouldNotSetReturnDueToNoLastCallException: ‘Could not find a call to return from.”
Typically, it seems to occur in one of two circumstances: substituting a concrete class and partially substituting a concrete class; that is:
var myMock = Substitute.ForPartsOf<MyClass>();
Or:
var myMock = Substitute.For<MyClass>();
Why?
If you were to manually mock out an interface, how would you do that? Well, say you had IMyClass, you’d just do something like this:
public class MyClassMock : IMyClass
{
// New and imaginative behaviour goes here
}
All’s good - you get a brand new implementation of the interface, and you can do anything with it. But what would you do if you were trying to unit test a method inside MyClass that called another method inside MyClass; for example:
public class MyClass : IMyClass
{
public bool Method1()
{
int rowCount = ReadNumberOfRowsInFileOnDisk();
Return rowCount > 10;
}
public int ReadNumberOfRowsInFileOnDisk()
{
// Opens a file, reads it, and returns the number of rows
}
}
(Let’s not get bogged down in how realistic this scenario is, or whether or not this is good practice - it illustrates a point)
If you want to unit test Method1(), but don’t want to actually read a file from the disk, you’ll want to replace ReadNumberOfRowsInFileOnDisk(). The only real way that you can do this is to subclass the class; for example:
public class MyClassMock : MyClass
You can now test the behaviour on MyClass, via MyClassMock; however, you still can’t* override the method ReadNumberOfRowsInFileOnDisk() because it isn’t virtual. If you make the method virtual, you can override it in the subclass.
The same is true with NSubstitute - if you want to partially mock a class in this way, it follows the same rules as you must if you would to roll your own.
Footnotes
* There may, or may not be one or two ways to get around this restriction, but let’s at least agree that they are, at best, unpleasant.