The It.Is and It.IsAny methods of the Moq unit-testing framework and why your "partial mocking" might do not work
Unit testing existing code can sometimes be challenging, especially when dealing with classes that contain too much functionality. In such cases, the Single Responsibility principle was not in focus.Refactoring the code can be an issue, so you will have to unit-test the class as it is. Additionally, we do not want to “mock” the interface of a class, which would mock all its methods, but the class itself.
Such classes often call methods of the same class, so the task is to mock only a few of the methods, not all of them, since we also want to unit-test some of them. The feature of partially mocking the methods of a class is called partial mocking. The methods that we want to mock, have to be defined as virtual, so that Moq can override them.
There are some great examples in StackOverflow on how to achieve that. However, in this article, I would like to focus on an issue that caused my partial mocks not to work and show you how I fixed it.
Here is a simple example of how the partial mocking works:
var mockedCustomer = new Mock<Customer>();
mockedCustomer.CallBase = true; // If true, calls the real method of the object, if this method was not mocked.
mockedCustomer.Setup(x => x.GetAddresses()).Returns(new List<Address>()); // This method is getting mocked
mockedCustomer.Object.GetAgeInYearsAndMonths(); // The real method is getting called
The problem I faced was that even though I mocked some of the class methods, the real implementations of these methods were getting called. After a lot of experimenting I found out that the It.Is method of Moq Framework was the issue. Consider the following example, where the GetAddresses method expects a flag-string to be passed, and this flag has to have a specific value:
mockedCustomer.Setup(x => x.GetAddresses(It.Is<string>(x => x == "privateAddresses")).Returns(new List<Address>());
The previous example was causing the mocked method not to get called, even when I was using the CallBase = true
flag. When I refactored the code to use the It.IsAny
method of Moq, the mocked method worked! For that, I had to do the check I did in It.Is
inside the Returns method.
mockedCustomer.Setup(x => x.GetAddresses(It.IsAny<string>()).Returns<string>(s => {
if (s == "privateAddresses") return new List<Address>());
return null;
}
I hope this article helped you if you faced the same problem as I did. Any comments are welcome.