LIFO makes sense when overwriting, thanks for the clarification. Is there a way to clean up all previously set up returns, without the need to overwrite them all?
Thanks for the article links. From my perspective however both Hadi and Mark talk only about issues with the strict mocks without mentioning any issues with the dynamic ones.
My main concern is debugging. Consider the following code:
public bool DoesUserExist(int userId)
{
var user = this.userRepository.Read(userId);
if (user.Id == 0)
{
return false;
}
return true;
}
[TestMethod]
public void UserExistsTest1()
{
UserRepository userRepository = Substitute.For<UserRepository>();
userRepository.Read(1000).Returns(new User() { Id = 1000 });
bool actual = DoesUserExist(10000);
Assert.True(actual);
}
[TestMethod]
public void UserExistsTest2()
{
UserRepository userRepository = Substitute.For<UserRepository>();
userRepository.Reads(1000).Returns(new User() { Id = 1000 });
bool actual = DoesUserExist(1000);
Assert.True(actual);
}
Both tests will fail on assertions. Since the tests are actually executing fine, but fail on assertions, your first thought would most likely be that the tested code is the problem - after all, it doesn't satisfy the tests. Once you spend your time overanalyzing DoesUserExist(), you can't really find any issue with it. You then turn to each test, spend more time analyzing them to find that you made some trivial typos. If the mocks were strict, you'd immediately know where the problem was.
A similar situation would occur if you modified the code being tested to use some undefined variation of the Read method.
If the assertion passes, it's even worse. The dynamic nature of the mock hides the fact that the test is buggy, and that it succeeds every time.
I'm thinking it would be a good idea to use ReceivedCalls() to debug such cases, though you need to know about the dynamic nature of the framework, which is not always apparent to e.g. new developers working on a legacy system.
My previous examples with SetupDelay and 'd1 == d2' are both code being tested. I'm especially interested in what the framework returns for the unexpected calls. If I have something like:
IMockedClass mockedObject = Substitute.For<IMockedObject>();
IMockedClass o1 = mockedObject.ReturnObject(1);
IMockedClass o2 = mockedObject.ReturnObject(1);
IMockedClass o3 = mockedObject.ReturnObject(2);
IMockedClass o4 = mockedObject.ReturnDifferentObject();
What are the relations between the returned objects? Are they the same objects? Different, but equal objects? Non-equal objects?
Thank you.