Moq, argument matching and Times.Never

Consider the following test code that is using Moq:

private void VerifyRequestWasNotLogged()
{
	_logRepository.Verify(
		r =>
			r.LogRequest(
				It.Is<RequestLogData>(
					l =>
						l.CurrentNumber == 0 && l.OriginalNumber == 0 &&
						l.Extension == string.Empty && l.OrderId == null &&
						l.OrderRef == null &&
						l.Url == Url)), Times.Never, "Wrong request.");
}

Not going too much into details, what this method does is verify that _logRepository.LogRequest was never called. The tests that were using this check were always green, but after some time, we found out that there actually are entries in the logs that were supposed not to be there. Can you spot the problem with this code?

In our case, there was a change in the code that changed the default value of RequestLogData.Extension property from string.Empty to null. Quite an innocent one and it didn’t break the tests. However, looking at the check above, there is a condition “l.Extension == string.Empty”. So the verification will never throw an exception, even if there is an undesirable call to _logRepository.LogRequest somewhere in the code under test. To the verification, the method is never called.. with the data specified 🙂

Looking at this and many more examples that I’ve seen, I’d say that Times.Never should rarely be combined with argument matching. Or, at least, the matching rules should be as loose as possible to avoid situations where it is not clear why it passes – because the method is not never called, or because the arguments don’t match on the called methods.

Have you ever been bitten by tests like this one? 🙂


Leave a comment