Skip to main content

Section 11.1 Test doubles

We will start by clarifying some terms.
A test double is an object used in a test, that is not the subject of the test, and is there instead of a "real object". Test doubles allow us to test the behavior of our subject without needing to worry about the implementations of other objects that our subject needs to do its work.
As an example, recall our test of the Summary class:
public void correctlySummarizeOneCourse() {
  List<Grade> grades = List.of(new Grade("A"));
  Iterator<Grade> reader = grades.iterator();
  Summary summary = new Summary(reader);
  assertEquals("Courses: 1\nGPA: 4.00\n",
               summary.invoke());
}
The reader object we created is in effect a test double. The summary class is the one we want to test. But it relies on an iterator, and so we create an iterator to give it. We don’t have to give it the "real" iterator that we use in the actual program, which is GradeReader. Instead we make a small "silly" iterator that is good enough for our testing.

Subsection 11.1.1 Test double classification

Test doubles are classified into several types:
Dummies are objects that are there purely because we needed some object in order to get the code to execute. But none of their fields or methods are ever accessed.
Stubs are objects that have specific values or behavior to them, and their goal is to drive the execution of the test’s subject in a particular direction.
Spies are objects that behave like stubs, with prescribed behavior to them, but they also remember that they were called, how many times they were called etc.
Mocks are objects that behave like spies, but further they know what is supposed to happen in the test (e.g. they know this method should be called in such a way etc) and we can ask them about it as part of verifying our test.
Incidentally, when we casually talk about these things we typically call all of them "mocks". I guess it’s easier to say than trying to distinguish between the different cases, and most of the time we don’t care about how best to name the thing as long as it’s doing its job.
Going back to our extremely simple Summary test above. If the line Summary summary = new Summary(reader); was basically the end of the test, then reader behaves like a dummy: We need it in order to call the constructor, but it doesn’t get used in anyway.
If we consider the next line in our test, where we call invoke and then test something about it, then reader behaves more like a stub: It drives the execution of Summary in a particular way, carrying out a single loop. It’s not a pure stub (we’ll see what that would have been like below) but it’s close enough.
If after that assertion we could ask the reader object whether its methods had been called and how, that would have made it into a spy.
We’ll discuss mocks when we talk about mocking frameworks. But first, let’s see what a manual test-double might have looked like.