I’ve been trying out different isolation frameworks lately and reading about their features and differences. In terms of feature, most constrained isolation frameworks are almost the same. But there are couple of important points about naming and readability that I think worth paying attention to. I’ve used Moq in my previous projects, but recently, I’ve realized that frameworks such as FakeItEasy and NSubstitute are a better choice. So I made a switch to FakeItEasy. In this post I explain my reasons for making that transition.
Moq’s naming is confusing
Naming tests and its constituent parts are important. The reason is that how we name things will shape how we think about them and how we use them as a result. If we take a look at testing nomenclature, we have at least three important terms, Fake, Stub and Mock. It’s accepted that each one of them has different meaning and should be used in different circumstances.
Fake: An object that can be used in place of another object. Fake is a general term that can be used for both stubs and mocks. But fakes can later becomes stub or mock, depend on how we decide to use them.
Stub: An object that get passed to another as dependency which doesn’t have any real implementation. We can change the internal implementation of its parts to return different results so we can test different parts of our app. It only help us in writing tests, it doesn’t do anything else in and of itself and can’t make our test fail. That is it doesn’t assert anything about itself.
Mock: An object that tracks its interaction with other objects and let us test our assumptions about those interactions. In other word it has behavior verification and can pass or fail our test based on its expectations. Mock is less prevalent than stubs, because testing system state changes and return values are preferable to testing internal interactions.
But Moq throws the term Mock all over the place. We always new up a Mock
whether we want to use it as a stub or as a mock. Let me clarify what I’ve said with an example.
In the above example the test DetailPassTheCorrectIdIntoPortfolioByIdQuery
use a mock called mediator.
It’s a mock because later the object is used to verify that correct PortfolioId
has sent to its Send method. On the other hand, the test DetailReturnsCorrectView
use a stub called mediator.
It’s a stub because it only served as a replacement with some tweaked behavior. But as you can see in second example, we still new up a mock, and this can become confusing. Now let’s take a look at FakeItEasy and NSubstitude.
The code above doesn’t use the word mock any place in our code. It’s our usage that determines whether it’s a mock or a stub. The reason that knowing the difference and avoiding confusion is important is that we should have one assert per test, and here mock object makes the assertion for us. So by extension we could say, we should have only one mock object per test. But when it comes to stubs, it acts only as a replacement and we can have many stubs in one unit test.
Moq is not as readable as FakeItEasy and NSubstitude
Unit tests can act as document for our code. It it can show us how a particular piece of code works and how it should behave. If we believe that about unit tests, then it’s reasonable to say things that makes our unit tests less readable is kind of a waste of our efforts. Let’s compare the readability of these frameworks.
In my opinion the readablity of FakeItEasy and NSubstitude is far superior to Moq. NSubtitude syntax is more terse, but I prefer FakeItEasy because the AAA-syntax is more consistent and it still readable.
FakeItEasy and NSubstitude have better documentation and simpler to use
Last point to touch on is documentation and ease of use. NSubstitude documentation is very good. FakeItEasy documentation is good enough, but not as good as NSubstitude. In terms of ease of use I think NSubstitude is the simplest and the syntax is intuitive, FakeItEasy syntax is both intuitive and consistent.
Summary
This post discussed why naming and readability matters in our tests. It also showed what other isolation frameworks has to offer and the value of switching to these frameworks. There is a lot of bad terminology surrounding unit testing. From TDD which testing is not its main purpose but design, to mocking frameworks which is again not a good name. I prefer what Roy Osherove calls them is his book, that is isolation frameworks. I highly recommend reading Roy Osherove’s book The Art of Unit Testing.
Interesting post, Hamid. I’m a Moq user because that was the framework decided upon before my time at my current place of work.
I doubt mine will be a popular opinion, but your two unit tests to me are essentially the same test – they both test what happens when you call the Detail() method on the PortfolioController. They each test only half of the actual “unit” being tested. Together they provide a full verification of the unit.
I much prefer the idea of testing one “unit” per test. That might involve multiple asserts and verify’s, but when classes are maintained well and kept small it doesn’t mean having large tests.
As part of this I typically make use of the TestInitialize and TestCleanup attributes (TestFixtureSetUp and TestFixtureTearDown if you’re an NUnit user). The initialize method will create all of the mocks required for the system-under-test – their behaviour can be customized in the individual tests if required. The clean up method calls VerifyNoOtherCalls() on all of the mock objects.
By using the initialize and clean up methods it allows me to remove much of the boilerplate code from the test methods themselves where appropriate. Each test then contains the code to setup it’s conditions, the verification of all mock invocations and the direct assertions. Also, by calling VerifyNoOtherCalls() on each of the mocks it will ensure that not only are the expected calls made to each dependency, but also that there are no unexpected calls to the dependencies.
I’ve looked at NSubstitute and FakeItEasy and I can’t see that they provide an equivalent for VerifyNoOtherCalls() and for me that’s a deal breaker.