In the last couple of months I had to use the
NSubstitute library. On their github page they define the library as "a friendly substitute for .NET mocking libraries". Essentially it is a mocking library but slightly different. In the past I have used Moq which is considered a high quality mocking library in the .NET world and while unit testing is probably my weakest side as a (business software) developer I have no reason to doubt that. That being said NSubstitute beats Moq so hard it is not even a competition.
Lets start with a simple comparison. Here is an example for testing an Add method with Moq
//Create the mock
var mockCalculator = new Mock<IClaculator>();
//Set the mock behavior
mockCalculator.Setup(c => c.Add(1, 2)).Returns(3);
//Extract the actual object that implements the interface
var calculator = mockCalculator.Object;
//We pass the calculator to the object under test that depends on it
//...
//Assert that the Add method was called with specific arguments
mockCalculator.Verify(c => c.Add(1, 2));
Moq is a high quality library with all the necessary features, static type checking, good naming and reasonable readabily. However lets see how NSubstitute does the same thing.
//Create the mock (or rather the substitute)
var calculator = Substitute.For<ICalculator>();
//Set the behavior
calculator.Add(1, 2).Returns(3);
//Pass the calculator object to the object under test that depends on it. No need to extract it. There is only one object that is used for configuration and substitute.
//...
//Assert that the Add method was called with specific arguments
calculator.Received().Add(1, 2);
Note that the mockCalculator object required for setup and assertions is gone completely. In addition we use actual method calls instead of lambdas to set the behavior of the mock.
I think that
code readability is greatly affected by how much certain code looks like an English sentence. Therefore I think that NSubstitute is superior based on the fact that it is far more similar to an English sentence than the equivalent Moq code. Just like Moq NSubstitute is statically typed. In addition NSubstitute allows me to literally paste code from the implementation in the unit tests and add a .Returns(something) as long as the naming of the arguments follows the same convention. The test code with Moq can get quite hairy if there are additional things to set via lambdas.
However I am not impressed so much by the readability of NSubstitute code but by the way they achieve it. As of time of writing NSubstitute is the third most beautiful programming hack I have seen. Here is my top 3:
-
Using iterators to do asynchronous programming (video deals with C# but the concept is decades old)
-
Carmack's reverse (not invented by John Carmack)
- NSubstitute
So how does NSubstitute work? Just like other mocking frameworks it creates an object that implements the interface or derives from the class while implementing/overriding methods that should be stubbed. The implementation in the method stub conforms to the mock configuration. How this thing works with Moq is clear. The setup method returns an object on which the Returns method can operate and in this way instruct the framework what it should do. Lambdas are not executed but the expression tree is inspected to extract the configuration information. When I first saw NSubstitute I was like - "How the fuck can the Returns method work on an int object since Add returns an int?". I reasoned that the only way would be for the stub to track the calls and so when Returns is called the framework knows that the last call was Add so it knows that the Returns applies to this specific call. I immediately dismissed the conclusion as too absurd for the real world and thought that there must be some reflection/IL magic at work. As it turns out NSubstitute does in fact use a static stack to track the method calls and applies corresponding returns to the last method on the stack. You can call Returns on any int but it will be applied to the Add method. The setup code above is equivalent to this:
//Set the bahavior
calculator.Add(1, 2);
0.Returns(3);
The last method on the stack was calculator's Add with arguments of 1 and 2 so this is what Returns is applied to. The type (int) is only used for type checking. In a normal library this would be unacceptable. This is how we end up with libraries that allow for mistakes. The more room for errors you leave the more errors you get. However there is something special about NSubstitute - it is a unit testing library. By definition each usage of a method from NSubstitute is covered by a unit test. If someone gets the order of method calls wrong he will get a failing test immediately. The designers optimized for the specific use case and achieved remarkable readability. For the record I do not know if the library authors invented this approach but I have not seen it elsewhere. The NSubstitute library is so great it inspired me to start writing unit tests for a project that did not previously have unit tests. Ten years of industrial experience could not convince me that maintaining unit tests is worth it for most projects but this library is so great that for the first time ever I am writing unit tests for fun and not because I am required to.