For a long time I have been fan of mocking frameworks like Moq and NSubstitute. These libraries seems indispensable while unit-testing. They allow us to easily generate subs and mocks and assert that certain interaction between components took place.
NOTE: If you do not remember difference between stub and mock please read this Martin Fowler article. In short mocks are used to test interactions between components (a method was called, a property was set) while stubs are used as dumb implementations of component dependencies (they usually either do nothing or provide some preset data).
But recently, after reading volume 1 of Elegant Objects which by the way I strongly recommend, I changed my mind. In one of the chapters author presents the idea that every interface should have an associated fake object. A fake object is a simple but working implementation of an interface and resides in the same source code file as the interface itself. Fake objects serve two purposes. First, they are example implementations of interfaces that show users how the interfaces should be implemented. And second they can be used as stubs and mocks in unit-tests.
Of course this idea seemed a bit extreme to me, so I decided to go with a bit more evolutionary approach. I slowly replaced all mock object that I had in my unit-tests with fakes (I put all fakes in my unit test projects - but I am still thinking that maybe they deserve a project of their own). During this process all interaction testing assertions that are usually performed using mocking frameworks were replaced by behaviour testing assertions on fake objects.
It will be the best to illustrate this process using an example.
Say we have a simple component
publishes two events (order is not important):
A “classic” unit test for this component using NSubstitute could look like this:
I am sure you have seen a lot of tests like this.
The key points are: Your create mocks and stubs using your
favourite mocking library in the test constructor or setup method.
In the arrange (given) part of the test you define mocks and stubs
behaviour using library specific syntax. Here e.g. we are capturing
argument passed to
Publish method for later use:
In the assert (then) part of the test we use again library specific syntax to check that a method on a mock was called with given set of arguments.
This approach is fine but it has some disadvantages:
It makes your tests very brittle. For example if I add a new method on
PublishAll(events)that allows me to publish all events at once and refactor
EventPublishingComponentto use it then
EventPublishingComponenttests would stop working. The main problem here is that my tests check internal interaction between components (was method
Publishcalled?) instead of checking external behaviour of the system (was event published?).
Mocking library is another tool that you must learn. And please remember that most of the developers are not too eager to read documentation. Time presumably saved by using mocking library will be lost on reading StackOverflow answers and on fighting with the library itself (ever have a problem that your stub does not return intended value?).
It makes your tests less readable. I must admit that NSubstitute is a huge improvement over Moq in terms of readability but it still introduces a lot of visual noise in the test code. For example do see all those
)in the code below:
Now let us see how our test can look like if we use fakes instead:
To make this test compile we also need to write a fake for
EventPublisher interface. Please keep in mind that fake is a simple
but working implementation of the interface:
I am sure that after seeing both versions of the test you agree with me that both are quite short and readable, yet the second version does not have the earlier mentioned disadvantages. Now you may rightly say that with the second approach you are forced to create fakes for almost all interfaces in your application. You are right, but you actually want to create fakes. Here is why:
Fakes are like TDD for your interface design. By creating a fake you actually check how difficult it is for a client of your API to provide an implementation. A fake too big or too difficult to implement is a sign that maybe your interface is doing too much. Also fakes can be treated as “reference implementations” of interfaces and as such they are part of your API documentation.
Writing a fake is a one-time effort. After fake is written it can be reused across many tests. Compare this with subs and mocks that you need to setup every time you want to use them.
Now it is time for a more real world example. As you probably heard Performance is a feature but logging can also be a feature. Imagine an application where we must log every failed login attempt. Since this is a business requirement we want to code it as an acceptance test. How difficult it can be to check that one method call was performed:
In practice it can be more difficult than it seems especially if you use
notoriously hard to test
ILogger hard to test?
ILoggerinterface contains only three methods (source code here) rest of its functionality is provided via extension methods.
Extension methods that operate on
ILoggeroften create wrappers around original arguments using classes like
FormattedLogValues. Most of these wrapper classes does not overload
GetHashCoderendering argument matchers from mocking frameworks useless.
No easy access to the logged message. Only method responsible for actual logging on
To gain access to the logged message we must either dig
All this causes that naive testing aproachs like this fail:
And how they fail? With confusing error messages like this one:
Not very helpful, isn’t it?
If you really want to test
ILogger using NSubstitute you must
use the following code:
Did I say something earlier about unreadable tests and a lot of visual noise caused by mocking frameworks? Now you can see it with your own eyes!
Now it is time for our second approach using fakes. First we create a fake logger:
Notice that we did not implement all methods of
For external interfaces that are not under our control we should
implement just enough functionality in our fakes to make them usable.
Now it is time for writing actual test:
Wow! Test is short, readable and simple. Exactly what I was looking for.
I hope that this blog post persuaded you to start using fakes in your unit tests. At least you now know that you have a good alternative to mocking frameworks.
Sample source code (with a bit more complicated example): GitHub.