Section 11.2 Manual Mocking
Many times you can write some manual mocks easy enough. Let’s do this for example for our
Summary
test. Here is how I would like the test to look like:public void correctlySummarizeOneCourse() {
OurTestDouble reader = new OurTestDouble();
Summary summary = new Summary(reader);
assertEquals("Courses: 1\nGPA: 4.00\n",
summary.invoke());
assertEquals(2, reader.timesHasNextCalled);
assertEquals(1, reader.timesNextCalled);
}
So I am creating an instance of our test double. This is a test double very specific to this test, so it’s a private inner class. Then I create the
summary
instance and call invoke
. Then I ask the reader
object how many tines hasNext
was called and how many times next
was called. And here is its implementation:private static class OurTestDouble implements Iterator<Grade> {
int timesHasNextCalled = 0;
int timesNextCalled = 0;
public boolean hasNext() {
timesHasNextCalled += 1;
return timesHasNextCalled < 2;
}
public Grade next() {
timesNextCalled += 1;
return timesNextCalled == 1 ? new Grade("A") : null;
}
}
So all this double does is return the grade A the first time it’s called, then nothing afterwards. And it simply keeps track of the times it’s called.
- By having the type
Iterator<Grade>
that theSummary
constructor needs, it acts as a dummy. - By returning the grade once and then nothing, it acts as a stub.
- By remembering how many times it was called, it acts as a spy.
If we wanted it to act as a mock, we could add a method called:
public boolean verify() {
return timesHasNextCalled == 2 && timesNextCalled == 1;
}
And replace the last two lines of our test with:
assertTrue(reader.verify());
And then we can make our two
times...
fields private. This way it’s only the mock that knows what should happen. When you reach the point where you want that kind of behavior, using the mocking frameworks is typically cleaner. We’ll take a look at this in the next section. But for now, creating these mocks isn’t too hard:- Implement the appropriate interface
- Make the methods behave as you would have expected the real objects to behave for this test
- Decide if you should keep track of calls via a counter or a list of arguments.