Core Java

Test Doubles: mocks, dummies and stubs

Most classes have collaborators. When unit testing, you usually want to avoid using real implementations of those collaborators to avoid test brittleness and binding/coupling, and instead use Test Doubles: Mocks, Stubs and Doubles. This article references two existing articles on the subject: Mocks Aren’t Stubs, by Martin Fowler and The Little Mocker, by “Uncle” Bob Martin. I recommend them both.

Terminology

I am going to borrow a term from Gerard Meszaros’s book xUnit Test Patterns. In it, he introduces the term System Under Test (SUT), that is, the thing we’re testing. Class Under Test is an alternative that is more applicable in the Object Oriented world, but I’ll stick with SUT since Fowler also does.

I will also use the terms state verification and behavior verification. State verification is verifying code worked correctly by examining the state of the SUT or it’s collaborators. Behaviour verification is verifying that collaborators were called or invoked in the way we expected.

Test Doubles

OK, back to how to deal with collaborators of the System Under Test. For each collaborator of the SUT, you could use the real implementation of that collaborator. For example, if you have a Service that collaborates with a Data Access Object (DAO), as in the WidgetService example below, you could use the real DAO implementation. However, it very likely goes against a database, which is definitely not what we want for a unit test. Also, if the code in the DAO implementation changed, it could cause our test to start failing. I personally do not like tests starting to fail when the code under test itself did not change.

So, instead we can use what are sometimes called Test Doubles. The term Test Doubles also comes from Meszaros’s xUnit Test Patterns book. He describes them as “any object or component that we install in place of the real component for the express purpose of running a test”.

In this article, I will cover the three main types of test doubles that I use: Mocks, Stubs and Dummies. I will also briefly cover two that I rarely explicitly use: Spies and Fakes.

1. Mocks

First, “mock” is an overloaded term. It is often used as an umbrella term for any test double; that is, any type of object used as a replacement for a real collaborator in a class under test. I am comfortable with this since most mocking frameworks support most of the test doubles discussed here. But, for the purposes of this article, I will use mock in its stricter, more limited meaning.

Specifically, a mock is a type of test double which uses behavior verification.

Martin Fowler describes mocks as “objects pre-programmed with expectations which form a specification of the calls they are expected to receive”. Where as Uncle Bob says that a mock spies on the behavior of the module being tested and knows what behavior to expect. An example may make it clearer.

Imagine this implementation of a WidgetService:

public class WidgetService {
    final WidgetDao dao;

    public WidgetService(WidgetDao dao) {
        this.dao = dao;
    }

    public void createWidget(Widget widget) {
        //misc business logic, for example, validating widget is valid
        //...

        dao.saveWidget(widget);
    }
}

Our test might look something like this:

public class WidgetServiceTest {

    //test fixtures
    WidgetDao widgetDao = mock(WidgetDao.class);
    WidgetService widgetService = new WidgetService(widgetDao);
    Widget widget = new Widget();

    @Test
    public void createWidget_saves_widget() throws Exception {
        //call method under test
        widgetService.createWidget(widget);

        //verify expectation
        verify(widgetDao).saveWidget(widget);
    }
}

We created a mock of WidgetDao, and verify that it was called as we expected. We could also have told the mock how to respond when it was called; this is a big part of mocks, allowing you to manipulate the mock so you can test a specific unit of your code, but in this case it isn’t necessary for the test.

Mocking Frameworks

In this example, I am using Mockito for the mocking framework, but there are others in the Java space too, including EasyMock and JMock.

Roll-your-own mocks?

Note that you don’t have to use mocking frameworks to use mocks. You can write mocks yourself too, and even build the assertion in to the mock. In this case for example, we could create a class called WidgetDaoMock that implements the WidgetDao interface, and whose implementation of the createWidget() method simply records that it was invoked. You can then verify the call was made as expected. Still, modern mocking frameworks make this kind of roll-your-own solution largely redundant.

2. Stub

A stub is an object that ‘stubs out’, or provides a greatly simplified version of, the implementation for the purposes of testing.

For example, if our WidgetService class now also relies on a ManagerService also. See the standardize method here:

public class WidgetService {
    final WidgetDao dao;
    final ManagerService manager;

    public WidgetService(WidgetDao dao, ManagerService manager) {
        this.dao = dao;
        this.manager = manager;
    }

    public void standardize(Widget widget) {
        if (manager.isActive()) {
            widget.setStandardized(true);
        }
    }

    public void createWidget(Widget widget) {
        //omitted for brevity
    }
}

And we want to test that the standardize method “standardizes” a widget when the manager is active, we could use a stub like this:

public class WidgetServiceTest {

    WidgetDao widgetDao = mock(WidgetDao.class);
    Widget widget = new Widget();

    class ManagerServiceStub extends ManagerService {
        @Override
        public boolean isActive() {
            return true;
        }
    }
    @Test
    public void standardize_standardizes_widget_when_active() {
        //setup
        ManagerServiceStub managerServiceStub = new ManagerServiceStub();
        WidgetService widgetService = new WidgetService(widgetDao, managerServiceStub);

        //call method under test
        widgetService.standardize(widget);

        //verify state
        assertTrue(widget.isStandardized());
    }
}

Where as mocks are generally used for behavior verification, stubs can be used for either state or behavior verification.

This example is very basic, and could have been done using a mock too, but stubs can provide a useful approach for test fixture configurability. We could have parameterized ManagerServiceStub so that it takes a value for the “active” field as a constructor argument and hence can be reused for the negative test case. More complex parameters and behaviors can be used too. Other options are creating the stub as an anonymous inner class, or creating a base class for the stub, such as ManagerServiceStubBase, for others to extend. The advantage of the later is that should the ManagerService interface change, only the ManagerServiceStubBase class will break and needs to be updated.

I tend to use stubs a lot. I like to flexibility they provide in being able to customize the test fixture and the clarity they provide from being plain ol’ Java code. No need for the future maintainers to be able to understand a certain framework. Most of my colleagues seem to prefer using a mocking framework. Find what works for you and use your best judgement.

3. Dummy

As the name implies, a dummy is a very dumb class. It contains next to nothing, basically just enough to get your code to compile. You pass a dummy into something when you don’t care how it’s used. e.g. as part of a test, when you must pass an argument, but you don’t expect the argument to be used.

For example, in the standardize_standardizes_widget_when_active() test in the previous example, we still continued to use the mocked WidgetDao. A dummy may be a better choice since we never expect the WidgetDao to be used at all in the createWidget() method.

public class WidgetServiceTest {

    Widget widget = new Widget();

    class ManagerServiceStub extends ManagerService {
        @Override
        public boolean isActive() {
            return true;
        }
    }
    class WidgetDaoDummy implements WidgetDao {
        @Override
        public Widget getWidget() {
            throw new RuntimeException("Not expected to be called");
        }
        @Override
        public void saveWidget(Widget widget) {
            throw new RuntimeException("Not expected to be called");
        }
    }
    @Test
    public void standardize_standardizes_widget_when_active() {
        //setup
        ManagerServiceStub managerServiceStub = new ManagerServiceStub();
        WidgetDaoDummy widgetDao = new WidgetDaoDummy();
        WidgetService widgetService = new WidgetService(widgetDao, managerServiceStub);

        //call method under test
        widgetService.standardize(widget);

        //verify state
        assertTrue(widget.isStandardized());
    }
}

In this case, I created an inner class. In most cases, since the Dummy functionality rarely changes across tests, it makes more sense to create a non-inner class and reuse for all tests.

Also note in this case that using a mocking framework to create a mock instance of the class is also a viable alternative. I personally rarely use dummies and instead create mocks like this:

WidgetDaoDummy widgetDao = mock(WidgetDao.class);

Although admittedly it can be more difficult to throw exceptions when unexpected invocations do happen (it depends on your mock framework of choice), it does have the big advantage of brevity. Dummies can be long since they need to implement every method in the interface.

As with Stubs, Dummies can be used with either state or behavior verification.

Spies and Fakes

I shall briefly cover two other types of test doubles: Spies and Fakes. I say briefly because I personally rarely use these two types of doubles explicitly myself, but also because terminology can be confusing enough without introducing more nuances! But in the interest of completeness…

Spy

You use a Spy when you wanted to be sure that a method was called by your system. It can also record all kinds of things such counting the number of invocations, or keeping a record of the arguments passed in each time.

With spies though, there is a danger of tightly coupling your tests to the implementation of your code.

Spies are used exclusively used for behavior verification.

This type of functionality is also very well covered by most modern mocking frameworks.

Fakes

Martin Fowler describes fakes as follows: Fakes have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).

I personally rarely if ever use them.

Conclusion

Test Doubles are an integral part of unit testing. Mocks, Stubs and Doubles are all useful tools to have, and understanding the difference is important.

A mock in the strictest sense of the word is simply a double that uses behavior verification; that is expectations on a double are specified and then verified when the SUT is called. However, the work mock has also grown to more generally describe any of the doubles described here, and indeed most modern mocking frameworks can be used in that general way.

Finally, which type of double should you use? It depends on the code under test, but I recommend being guided by whatever makes the intent of your tests clearest.

Sources

Reference: Test Doubles: mocks, dummies and stubs from our JCG partner Shaun Abram at the Shaun Abram blog blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button