Unit Testing with Stubs and Mocks |
|

I was on site with some clients the other day, and they asked me about unit testing and mock objects. I decided to write up some of the discussion we had as a tutorial on creating dependencies (collaborators) for unit testing. We discuss two options, stubbing and mock objects and give some simple examples that illustrate the usage, and the advantages and disadvantages of both approaches.
It is common in unit tests to mock or stub collaborators of the class under test so that the test is independent of the implementation of the collaborators. It is also a useful thing to be able to do to control precisely the test data that are used by the test, and verify that the unit is behaving as expected.
Stubbing
The stubbing approach is easy to use and involves no extra dependencies for the unit test. The basic technique is to implement the collaborators as concrete classes which only exhibit the small part of the overall behaviour of the collaborator which is needed by the class under test. As an example consider the case where a service implementation is under test. The implementation has a collaborator:
public class SimpleService implements Service {
private Collaborator collaborator;
public void setCollaborator(Collaborator collaborator) {
this.collaborator = collaborator;
}
// part of Service interface
public boolean isActive() {
return collaborator.isActive();
}
}
To test the implementation of isActive we might have a unit test like this:
public void testActiveWhenCollaboratorIsActive() throws Exception {
Service service = new SimpleService();
service.setCollaborator(new StubCollaborator());
assertTrue(service.isActive());
}
...
class StubCollaborator implements Collaborator {
public boolean isActive() {
return true;
}
}
The stub collaborator does nothing more than return the value that we need for the test.
It is common to see such stubs implemented inline as anonymous inner classes, e.g.
public void testActiveWhenCollaboratorIsActive() throws Exception {
Service service = new SimpleService();
service.setCollaborator(new Collaborator() {
public boolean isActive() {
return true;
}
});
assertTrue(service.isActive());
}
This saves us a lot of time maintaining stub classes as separate declarations, and also helps to avoid the common pitfalls of stub implementations: re-using stubs across unit tests, and explosion of the number of concrete stubs in a project.
What's wrong with this picture? Well, often the collaborator interfaces in a service like this one are not as simple as this trivial example, and to implement the stub inline requires dozens of lines of empty declarations of methods that are not used in the service. Also, if the collaborator interface changes (e.g. adds a method), we have to manually change all the inline stub implementations in all the test cases, which can be a lot of work.
To solve those two problems we start with a base class, and instead of implementing the interface afresh for each test case, we extend a base class. If the interface changes, we only have to change the base class. Usually the base class would be stored in the unit test directory in our project, not in the production or main source directory.
For example, here is a suitable base class for the interface as defined:
public class StubCollaboratorAdapter implements Collaborator {
public boolean isActive() {
return false;
}
}
and here is the new test case:
public void testActiveWhenCollaboratorIsActive() throws Exception {
Service service = new SimpleService();
service.setCollaborator(new StubCollaboratorAdapter() {
public boolean isActive() {
return true;
}
});
assertTrue(service.isActive());
}
The test case is now insulated from changes to the collaborator interface that do not affect the isActive method. In fact, using an IDE, it will also be insulated from some changes in the interface that do affect the isActive method – for instance a name or signature change can be made automatically by the IDE in all test cases.
The inline stub approach is very useful and quick to implement, but to have more control over the test case, and ensure that if the implementation of the service object changes, the test case also changes accordingly, a mock object approach is better.
Mock Objects
Using mock objects (e.g. from EasyMock or JMock) we get a high level of control over testing the internals of the implementation of the unit under test.
To see this in practice consider the example above, rewritten to use EasyMock. First we look at EasyMock 1 (i.e. not taking advantage of Java 5 extensions in EasyMock 2). The test case would look like this:
MockControl control = MockControl.createControl(Collaborator.class); Collaborator collaborator = (Collaborator) control.getMock(); control.expectAndReturn(collaborator.isActive(), true); control.replay(); service.setCollaborator(collaborator); assertTrue(service.isActive()); control.verify();
If the implementation changes to use a collaborator differently, then the unit test fails immediately, signalling to the developer that it needs to be re-written. Suppose the internals of the service changed to not use the collaborator at all:
public class SimpleService implements Service {
...
public boolean isActive() {
return calculateActive();
}
}
The test above using EasyMock would fail with an obvious message saying that the expected method call on collaborator was not executed. In the stub implementation, the test might or might not fail: if it did fail the error meesage would be cryptic; if it did not, then it would only be accidental.
To fix the failed test we have to modify it to reflect the internal implementation of the service. The constant re-working of test cases to reflect the internals of the implementation is seen by some as a burden, but actually it is in the very nature of unit testing to have to do this. We are testing the implementation of the unit, not its contract with the rest of the system. To test the contract we would use an integration test, and treat the service as a black box, defined by its interface, not its implementation.
EasyMock 2
Note that the above test case implementation can be streamlined if we are using Java 5 and EasyMock 2:
Collaborator collaborator = EasyMock.createMock(Collaborator.class); EasyMock.expect(collaborator.isActive()).andReturn(true); EasyMock.replay(collaborator); service.setCollaborator(collaborator); assertTrue(service.isActive()); EasyMock.verify(collaborator);
There is no need for the MockControl in the new test case. Not a big deal if there is only one collaborator, like here, but if there are many, then the test case becomes significantly easier to write and read.
When to Use Stubs and Mocks?
If mock objects are superior, why would we ever use stubs? The question is likely to draw us into the realm of the religious debate, which we will take care to avoid for now. So the simple answer is, "do what suits your test case, and creates the simplest code to read and maintain". If the test using a stub is quick to write and read, and you are not too concerned about changes to the collaborator, or uses of the collaborator internally to the unit under test, then that is fine. If the collabrator is not under your control (e.g. from a third-party library), it may often be the case that a stub is more difficult to write.
A common case where stubbing is easier to implement (and read) than mocks is where the unit under test needs to use a nested method call on the collaborator. For example, consider what happens if we change our service, so it no longer uses the collaborator's isActive directly, but instead nests a call to another collaborator (of a different class, say Task):
public class SimpleService implements Service {
public boolean isActive() {
return !collaborator.getTask().isActive();
}
}
To test this with mock objects in EasyMock 2:
Collaborator collaborator = EasyMock.createMock(Collaborator.class); Task task = EasyMock.createMock(Task.class); EasyMock.expect(collaborator.getTask()).andReturn(task); EasyMock.expect(task.isActive()).andReturn(true); EasyMock.replay(collaborator, task); service.setCollaborator(collaborator); assertTrue(service.isActive()); EasyMock.verify(collaborator, task);
The stub implementation of the same test would be
Service service = new SimpleService();
service.setCollaborator(new StubCollaboratorAdapter() {
public Task getTask() {
return (new StubTaskAdapter() {
public boolean isActive() {
return true;
}
}
}
});
assertTrue(service.isActive());
there isn't much to distinguish between the two in terms of length (ignoring the code in the adapter base classes, which we can re-use in other tests). The mock version is more robust (for reasons discussed above), so we prefer it. But if we had to use EasyMock 1 because we were unable to use Java 5, things might be different: it would be quite a lot more ugly to implement the mock version. Here it is:
MockControl controlCollaborator = MockControl.createControl(Collaborator.class); Collaborator collaborator = (Collaborator) controlCollaborator.getMock(); MockControl controlTask = MockControl.createControl(Task.class); Task task = (Task) controlTask.getMock(); controlCollaborator.expectAndReturn(collaborator.getTask(), task); controlTask.expectAndReturn(task.isActive(), true); controlTask.replay(); controlCollaborator.replay(); service.setCollaborator(collaborator); assertTrue(service.isActive()); controlCollaborator.verify(); controlTask.verify();
The test is half as long again, and correspondingly harder to read and maintain. Things can easily get much worse in reality. In this case we might consider the stub implementation in the interests of an easy life. Of course the true believers in mock objects will point out that this is a false economy, and the unit test will be more robust and better for the long term than the test using stubs.
Similar Posts
- BeanInitializer: wiring dependencies in unit tests
- Setter injection versus constructor injection and the use of @Required
- OSGi Test Stubs 1.0.0.M1
- Countdown to Grails 2.0: Unit testing
- Enabling Test Driven Development in GWT client code









Carango says:
Added on January 15th, 2007 at 8:45 amHi,
Great article, thanks! Just to let you know, the last paragraph under the "EasyMock 2", the last sentence subtitle is cut.
Stephen Duncan Jr says:
Added on January 15th, 2007 at 6:14 pmAlso note that tools like EasyMock can be used to create dynamic stubs, which can be shorter, because you don't need to call verify, and won't check to see if the method is called (in case you don't want your test to care).
Colin Yates (blog author) says:
Added on January 16th, 2007 at 7:31 amNice article Dave.
I tend to prefer dynamic mocks simply because when doing TDD you don't always know the full API of the collaborator. If you are using stubs, this results in the horrible situation of having to go through unrelated tests and adding/removing/changing methods every time you change the API of a collaborator (add a new method etc.).
This isn't a problem with mocks because you only describe the bits that you are using…if a new method is added to the collaborator, who cares
Mocks also (IMO) lead to much more readable code as well, they also allow you to define the behaviour of multiple calls to the same method. Of course this can be done using stubs, but the code is much more complex.
Nice article.
redsolo says:
Added on January 17th, 2007 at 3:12 amThe best description of mocks/stubs/fakes/etc are available at Martin Fowlers http://www.martinfowler.com/articles/mocksArentStubs.html. He has a very good indepth explaination when to use mocks and when to use stubs. Mocks enforces another type of testing, a sort of behaviourtesting than what is normal with unit testing.
Tim says:
Added on January 24th, 2007 at 12:37 pmAs a jMock fan, I figured I'd post an example of the same test with jMock.
Assumes your test extends MockObjectTestCase
public void testActiveWhenCollaboratorIsActive() {
// Normally done in setUp
SimpleService service = new SimpleService();
Mock collaborator = mock(Collaborator.class);
service.setCollaborator((Collaborator) collaborator.proxy());
// Actual test
collaborator.expects(once()).method("isActive").will(returnValue(true));
assertTrue(service.isActive());
}
Screenedtwenty says:
Added on May 18th, 2007 at 7:23 pmFor anyone interested in more reading on Unit Testing, this may or may not answer more questions: http://www.coderslog.com/Unit_Testing_101
We might also be getting usage examples in the future in a separate article.
Thank you for your time.
Zuber says:
Added on March 11th, 2008 at 11:15 amColin, Why do we write java classes for Unit Testing?
So far there are lot of unit testing frameworks available today to choose from. Each stands with different approach.
But the matter of fact is, we still need to code. Can we not configure beans what we want to test and actual and expected results?
Imagine a world where we write unit tests, if it's not so complex, in xml instead of creating lot of classes.
So here is an idea for a framework where you write your unit test cases in xml and not create any classes.
The framework provides a helpers/wrappers around various unit frameworks like Junit, TestNG, Jmock etc.
It reads the definition of test case and data providers and generates TestCase using any of above specified Unit Testing frameworks and executes it. We can even set-up some callback mechanism for better control over automated behavior.
Your thoughts.
KV says:
Added on April 29th, 2008 at 6:19 pmIn our business group we are trying to embrace UNIT testing and doing our best to achieve a decent code coverage. However we are facing some interesting challenges. Initially we decided on the path of using stub data for Unit Testing (DB Unit is out of question at this point due to DB related constraints) and one of the challenges seems to be around creation and ongoing maintenance of test data. So here is a simple scenario
Lets say there are two components that make our simple system – Component A and Component B. Component A depends on Component B. From unit testing perspective, comp B developer has created a robust set of "stub" data to meet his testing needs. Since Component A collaborates with B, the developer who is authoring A wants to leverage the test data already written for B. Now this introduces data dependency because the unique id(s) & data used to drive B is now needed by A. This means 'A' and 'B' developer need to sit and talk about Data before they can author unit test cases. While this model might work when things are simple, some of us are asking questions like – will it scale? etc
That's said, if you could provide your thoughts, opinions, help for the following questions, it would help us move in the right direction
What is your opinion on using stub data for Unit Testing?
Is this the way to go or are there other better ways?
Does the stub based approach scale well when there are many components, developers etc (including offshore)
Is this just too brittle where the maintenance of test cases/data could become a chore?
Will this just introduce "communication & coordination" complexities?
Should we think about a interaction-based testing (mocks) instead of state-based testing (stub)
If so, any pit falls with this approach
Dave Syer (blog author) says:
Added on April 30th, 2008 at 6:43 amI think it sounds like you are thinking on the right lines, and all your concerns are valid. Stubs have their place, but sharing stubs across unit tests (your example of components A and B above) does not scale, precisely because of the brittleness you are worried about. I always define stubs as inner classes in the test that needs them – make them private and that way no-one can be tempted to share it and introduce an implicit dependency. The "test data" then gets maintained by the unit test owner, and there is no need to worry about it as a separate category of data. Base classes with empty methods are helpful to avoid cluttering up tests with implementations of many methods that aren't used.
Unit Testing says:
Added on July 28th, 2010 at 7:58 amI am a regular reader of this blog and the information here i get is unique and useful both. This information really help me in making my Unit Testing work far and far better then past.