Mocks vs stubs, the pros and cons of frameworks vs implementations

I will go right to the point here. In the title I have used the term "stub" in its broad sense. What I really mean is test double. However the term "test double" is really not that used. Stub is often wrongly used for any implementation of a class created to do testing. What the title likely should have said was mocks vs test implementations. In my post on why we do mocking I have listed a couple of different types of test doubles. Examples of this would be stubs, mocks, fakes or spies. In this post I will discuss the pros of cons of mocks and the other types of test doubles.

Test doubles are helpers that make it possible to control dependencies. So that the unit under test can be tested under certain circumstances. The unit we are testing can also be tested in full isolation if all of it's dependencies are test doubles. In this scenario, the only actual production code we are testing is the one in the unit, everything else is test doubles. Test doubles can also be great to trigger hard-to-create scenarios, which could be error scenarios such as timeouts or failed authentication responses.

Mocks

Mocks are a special type of test doubles. They usually require a framework and instead of having to write your own implementations you just leverage your mocking framework. It is easier to demonstrate this through an example. Below I have made a BoardGame class which has an IDice as a dependency. In this simple implementation all you can do is call RollDice() which in turn calls Roll() on the _dice object:

public class BoardGame : IBoardGame
{
    private IDice _dice;

    public BoardGame(IDice dice)
    {
        _dice = dice;
    }

    public int RollDice()
    {
        return _dice.Roll();
    }
}

Now if we wanted to test this class without a mocking framework we would likely write a test and a test-implementation (test double) like below:

[Test]
public void BoardGameReturns6WhenDiceReturns6()
{
    var boardGame = new BoardGame(new Always6DiceStub());
    Assert.AreEqual(6, boardGame.RollDice());
}

private class Always6DiceStub : IDice
{
    public int Roll()
    {
        return 6;
    }
}

This forces the dice above to always return 6. Giving us control over what the dice dependency does within our BoardGame class. The above is a simple stub implementation. Next, let us take a look at what this looks like with a mocking framework. Here I am using my favorite mocking framework - NSubstitute:

[Test]
public void BoardGameReturns6WhenDiceReturns6WithMocks()
{
    var dice = Substitute.For<IDice>();
    dice.Roll().Returns(6);
    var boardGame = new BoardGame(dice);
    Assert.AreEqual(6, boardGame.RollDice());
}

In the above I create a new substitute (mock). I then instruct it to return 6 whenever Roll() is called. Thereafter I use it as a dependency on my BoardGame and assert just like before. This requires a lot fewer lines than writing an implementation.

Most mocking frameworks are rather rich in features and can assert all sorts of results. Most can work as both stubs and spies. That is, they can return canned answers (stubs) and they can capture and report what calls they receive (spies). Most can also throw exceptions (saboteurs). Some mocking frameworks can also assert that they were called in the right order or with specific arguments.

So a mock is just a swiss army knife replacement for all other test doubles? Not exactly.

Mocks vs writing test implementations

Overall I do not see a huge difference between using mocking frameworks and writing implementations. You can accomplish the same thing (test your code) and it is just about the means to do it.

The first difference is that mocks require a framework. This could be Moq, Nsubstitute, Rhino mock or any other mocking framework. Writing your own implementations you do not need a framework. Is this a big deal? installing a nuget package is not. But you will also have to learn the framework and invest time in it. If you for some reason switch company or perhaps team, you may have to start all over learning a new framework. Mocking has a time investment which you will have to redo if you switch frameworks. Unlike creating your own implementations, which is just using your programming language natively. The learning curve is smaller and perhaps more intuitive if you use test implementations.

However writing test implementations can cause your code to be bloated with all kinds of implementations. If you are the type who writes a lot of unit tests you may end up with many different implementations. Often your test doubles are simple. Such as a stub. In the previous example that I made, the code for the mock was a lot smaller (arguably simpler) than the stub. In this situation it is hard to see why you would want to have an "extra" class for this.

Another difference is that our mocks reside within the test method and implementations outside (usually). Not only are mocks declared within the test method, but they are also configured in this scope. On the other hand other test doubles are declared within the test, but their logic lies elsewhere (in the implemented class). If you have several dependencies and wish to assert on most of them, this can take up a lot of lines within your test. This is worse when you do mocking. This can make it harder to understand what is going on in the test. However in the scenario where the amount of logic is small, it is great that the setup of the dependencies is within the code. This way you do not have to open other classes to understand your tests.

In short

If you don't want to read the whole article and jump right to the point, here is a summary:

  • Mocks require a framework
  • You have to learn and master the mocking framework (invest time)
  • Using mocks you avoid having extra test double classes for your tests
  • Setting up mocks and their expectations can take up a lot of space in your tests
  • With mocks, the expected behavior of your dependencies is within in your test - not in separate classes

Overall I think the biggest difference is that with mocking frameworks you make a time investment in learning your framework. In return you avoid having many test doubles in your codebase.

I hope you liked my post on the difference between using mocking frameworks and writing your own test implementations. Was there anything I missed? Let me know down in the comments.