◆ The point
We use mock objects to make testing easier, particularly when we need to add tests to existing code. There are a number of ways to use mock objects, so it can be diffi- cult to know where to start. Some of the terms are overloaded and can cause confu- sion, so it is a good idea to understand a few of the essential mock objects techniques and what distinguishes them from one another. In spite of this complex-
8 See www.objectmentor.com/resources/articles/ocp.pdf for a discussion of this design principle.
690 APPENDIX B Essays on testing
ity, EasyMock (www.easymock.org) provides the majority of what you need to take advantage of the various mocking techniques into your tests.
◆ The details
In 2000, Tim Mackinnon, Steve Freeman, and Philip Craig wrote the paper
“Endo-Testing: Unit Testing with Mock Objects,”9 in which they introduced the notion of mocking method invocations to facilitate isolated Object Tests. The term “mock object” refers to an object that stands in place of the object you would use in production: often it is an alternate implementation of a given interface. In particular, a mock object emulates the behavior of the real thing without duplicat- ing it entirely: for example, a method may return the same result every time, ignoring its parameters. The goal is to simulate as little of an object’s behavior as you need for the current test. What makes a mock object more than just a stub is that it also makes assertions about the way it is used. It expects a particular method invocation, or certain parameters, or a given sequence of method invoca- tions. To use a mock object, you instantiate it, tell it what to expect, supply it to the object under test, then ask it to verify whether the object being tested used it correctly. The term “endo-testing” refers to the fact that one passes a mock object as a parameter to the object under test, dynamically introducing assertions into the production code (testing from the inside) without leaving them there to be removed later. The rest of the testing we do can therefore be termed “exo-testing”:
testing from the outside. As mock objects have become more commonplace in Object Tests, an entire vocabulary has evolved around them, and we think that you ought to know these terms in order to better understand them and communi- cate your mock objects techniques to others.
First, we need to draw a distinction between mock objects and Mock Objects (www.mockobjects.org). The generic term—or simply “mocking”—refers to the general technique of substituting an alternative implementation of an interface (or individual method) in place of the production-quality collaborator for the purposes of testing an object.10 When testing JDBC client code, we say that we
“mock the database,” which may involve using a mock implementation of the Con- nection interface, serving up mock Statement objects. There are many ways to mock a method or an interface, as we will describe here. The term Mock Objects,
9 You can find the article online or in Extreme Programming Examined (Addison-Wesley, 2001), a collection of papers on Extreme Programming edited by Giancarlo Succi and Michele Marchesi.
10We have begun to call these kinds of objects “test objects,” especially when speaking, because it is diffi- cult to use a verbal cue to distinguish a “mock object” from a “Mock Object.”
TE AM FL Y
Team-Fly®
691 The mock objects landscape
with the words capitalized, makes us think of Tim, Steve, and Philip’s work: a spe- cific kind of mock implementation: one that knows how to expect to be used, and verifies that it was used that way. Much of what we discuss in this essay concerns the generic mocking technique, although we make heavy use of Mock Objects in our work.
The simplest mocking technique is a “stub.” The community is divided on whether stubs really support mocking, because they do nothing: a stub is an empty implementation of an interface. All its methods do nothing and return default values. It is there merely to allow the system to compile and execute. Test- Driven Development practitioners usually build a stub to implement an interface they have just decided they need. Stubs are of limited use in testing: mostly as a means of keeping an object out of the way. You may be testing a method that takes five parameters, but for this test you only need three of those parameters. The method under test throws an exception if you try to pass it null parameters, so to satisfy the method’s demands you pass it the simplest possible implementation of that parameter’s type: a stub. Some stubs turn into Null Objects,11 and others become Java interface adapters. Stubs are of limited use in testing because they return default values, such as 0, false, and null.
If you decide that the stub is not really a mocking technique, then the next-simplest mocking technique is the “fake.” A fake method is one that returns a fixed value every time it is invoked, and so a fake object is an object with fake methods. Faking a method makes it possible to test how the object under test responds to the vari- ous kinds of behavior it can expect from its collaborators. For example, an online store has a product catalog. The shopper may ask to display the details on a partic- ular product: she first browses the catalog and finds a link to the product she wants to see, then gets up and leaves her computer for ten minutes. In the meantime, the product manager pulls that product from the shelves, because it has not been sell- ing well.12 He executes the administrative command to remove the product from the catalog, but the shopper still has that link in her browser. When she clicks that link, which contains a product number of some kind to identify the product to dis- play, there is no product matching that product number. The system needs to be able to handle that, rather than blow up with an exception that the web container finally handles. There are two approaches to test this condition.
11http://c2.com/cgi/wiki?NullObject
12Yes, it would be better to pull the product off the shelves during off-hours, but this is an online store—
somewhere in the world one of their customers is awake and using their site.
692 APPENDIX B Essays on testing
A direct approach is to write the test using a product number that you know is not in the catalog; however, this forces you to deal with implementation details that need not concern you for the current test. It does not matter which product you try to display—it just matters what happens when that product is not in the catalog. A more robust testing approach involves using a fake catalog that always answers “no” when you ask it, “Does this product exist?” The idea is to test how the product display logic reacts to a nonexistent product without worrying about using “the magic product number with no associated product.” In your test fix- ture, use a FakeCatalog such as this one:
public class FakeCatalog implements Catalog { ...
public boolean exists(String productId) { return false;
} ...
}
Typically, a fake object returns hard-coded values, and so you would need to cre- ate a different class for each different way you want to fake a method. If you find yourself in this situation, you may decide to build a fake object whose hard-coded values you can set programmatically. Returning to our example, you may want to build a FakeCatalog to which you can add a known list of products, as opposed to the production catalog which retrieves them from a database.
public class InMemoryCatalog implements Catalog { private Map products = new HashMap();
public void addProduct(Product product) { products.put(product.getId(), product);
}
public boolean exists(String productId) { return products.containsKey(productId);
} ...
}
Now you can prime the catalog with a known list of products for each test without having to put data into database tables. The trouble with this kind of fake object is that it often leads the programmer to duplicate logic from the production-quality object. We already see that here: even though the code for the exists() method is different between the InMemoryCatalog and the production catalog (which checks a database table), the logic is duplicated: “a product exists if I can find its product ID.” This duplication may well be benign, but it does not take long to reach the
693 The mock objects landscape
point where you are no longer faking methods, but rather providing a production- quality alternate implementation of the interface to which the method belongs.
In this case, you might be tempted to build an in-memory repository for all your domain objects. Something that complex needs to be tested itself, and who wants to test the test objects? The key to faking methods and objects effectively is to keep the fakes dead simple: if they want to do something more complex than return one or two hard-coded values, then either the test is not narrowly focused enough or you need to use a more powerful mocking technique. There are two kinds of fake objects that deserve particular attention. They have evolved as common pat- terns for faking methods: the Spy and the Crash Test Dummy.
A Spy is a fake object that “gathers intelligence” about how it was used, then
“reports back” to the test. The test can then make assertions about what the Spy
“saw.” One common application of the Spy fake object pattern is when you test an event source (see recipe 14.2, “Test an Observable”). The Spy event listener col- lects the events it receives, then provides the test access to that collection. The test can then compare the events the Spy heard against the list of events that the test expects the event source to generate. As in our recipe on the subject, it is com- mon to use the Self-Shunt pattern in this case, so that the test case class itself becomes the Spy event listener. In general, you can use a Spy to track the order in which its methods are invoked, along with the parameters passed to each method.
This focuses your tests on the interactions between objects, without worrying as much about the way they implement those methods—you will test that separately.
As you will see, the Mock Object is, among other things, the überspy.
The Crash Test Dummy is a fake object that throws exceptions, rather than returning hard-coded values. You can use a Crash Test Dummy to verify how the object under test responds when its collaborator fails somehow. As we have writ- ten previously in this book (see recipe 2.11, “Test an object that instantiates other objects”), it is generally much easier to simulate failure conditions than it is to recre- ate them. Returning to our example, there is another way to signal that a product does not exist in the catalog: throw a NoSuchProductException.13 The production catalog performs a SELECT on the database, then throws NoSuchProductException if the ResultSet is empty. The corresponding CrashTestDummyCatalog simply throws the exception every time:
13We do not recommend using an exception this way, but you will come across it eventually, so you might as well know how to test it effectively.
694 APPENDIX B Essays on testing
public class CrashTestDummyCatalog implements Catalog { ...
public Product getProduct(String productId) { throw new NoSuchProductException(productId);
} ...
}
Just as with the previous FakeCatalog, this CrashTestDummyCatalog eliminates the need for your test to use the “magic product number with no product associated.”
No matter which product you try to find, the Crash Test Dummy will not find it.
This is a simple way to test how your objects react when their collaborators throw exceptions. These special kinds of fake objects are really not so special: they are merely specific ways to use fake objects. Beyond fakes, however, lie Mock Objects, which provide one very powerful additional feature that merits having them in their own category.
A true Mock Object is a self-verifying Spy. The primary advantage of the Mock Object over the Spy is that the former knows how you expect to invoke it—which methods, with which parameters, and in which order—and verifies how it was used when you invoke the verify() method. This simple change in approach makes your tests considerably easier to read and to maintain. Although a Spy can gather the same information as a Mock Object, it is still the test’s responsibility to interpret what the Spy “saw.” The test has less to do when using a Mock Object. It tells the Mock Object what to expect—how many times a method ought to be invoked, or which parameters to expect—and then passes the Mock Object into the object under test. After using the Mock Object, the test merely invokes ver- ify() and the Mock Object verifies that each of the test’s expectations were met.
The resulting design retains more of its natural encapsulation, as the Mock Object implementations do not need to expose the actual values they gathered to the test for verification. More than this, the Mock Object can fail fast—that is, because it knows the expectations placed on it, it can make assertions about how it is used as the test executes. As soon as one of those assertions fails, the test fails before the faulty code can do further damage, pointing directly to the problem. Not so with a Spy. Because a Mock Object can fail the moment one of its assertions fail, it is much more likely that the resulting error message indicates the cause of the prob- lem, eliminating the need to step through code with a debugger. A Mock Object, then, is a more highly encapsulated, fail-fast version of the already-powerful Spy object. For further information on the benefits of using Mock Objects, we recom- mend that you read the paper to which we referred at the start of this essay.
695 The mock objects landscape
Now that you have seen the landscape of test objects, which do you use? And when? There is a simple answer: it depends on the role the object plays in your test.
Some objects are involved because they need to provide services and some objects are involved because they need to provide data. For the ones that provide data, fake the data; for the ones that provide services, use a Mock Object. In the case of data, you just need an object to return predictable results, and so faking those results ensures predictability. For example, if you want to test how your business logic reacts when your data layer returns an empty list of products from the cata- log, then use a fake catalog that always returns an empty list of products. In the case of services, you only need to know that the object under test invokes its collab- orator’s methods with the correct parameters, so set that expectation on a Mock Object. For example, you want to verify that your data access logic correctly inserts a domain object into the database. In this case, tell a Mock PreparedStatement object which parameters to expect to receive on various invocations of set- String(), setInt(), setTimestamp() and so on, then pass the Mock Prepared- Statement to your data access logic for testing. These are the two prime examples of the mock objects approach on which we rely heavily in our work and which we use often in this book’s recipes.
Finally when it comes time to implement Mock Objects, we recommend using EasyMock (www.easymock.org), which uses a record-and-playback mechanism to provide dynamic Mock Object implementations of any interface. You can use this package to implement both fakes and Mock Objects proper: the only difference is whether you invoke verify() on the EasyMock object at the end of the test. (Easy- Mock does allow for “nice” controls, which do not fail fast, but we tend not to use them.) Using EasyMock drives you towards strong interface/implementation sep- aration, which is an improvement in most designs. There is much more we could write about mock objects (and about Mock Objects), but this is enough to get you on your way. The best way to understand them, as always, is to start using them.
NOTE In the time between writing this essay and sending the book to be printed, a new dynamic proxy-based mock objects package has appeared on the scene, called jMock (www.jmock.org). It picks up where EasyMock left off, as the EasyMock project went through a temporary lull in activity, between October 2003 and May 2004. Being so new, we do not have any experience using it, and so we cannot say much about it, but it does look promising and bears a look. If you have used EasyMock, then it is worth experimenting with jMock to see the difference. You may find you prefer jMock’s approach to that of EasyMock.14
14Note that EasyMock released version 1.1 in May 2004, and so now there is some competition in this
696
Reading List
697 Reading List
Java Testing
Dave Astels, Test-Driven Development: A Practical Guide (Prentice Hall PTR, 2003).
This is the first TDD-related book we have seen that uses a Swing application as its central example, rather than yet another web application. This is an excellent tour through all the basics of TDD, some JUnit-related tools, Mock Objects, and a fully developed example, test by test.
Johannes Link, Unit Tests with Java: How the Tests Drive the Code (Morgan Kauf- mann, 2003).This is another fine look at test-driven development using JUnit.
Vincent Massol and Ted Husted, JUnit in Action (Manning, 2004). This is a tutorial approach to JUnit with advice on testing a wide range of Java applications and components.
Andrew Hunt and Dave Thomas, Pragmatic Unit Testing in Java with JUnit (Pragmatic Programmers, 2003). From the authors of The Pragmatic Programmer (see the General Programming section). This is a no-nonsense look at unit testing using JUnit and Java, including a thorough description of testing with Mock Objects. It is an excellent com- panion to this book. (There is also a C#/NUnit version of this book.)
Richard Dallaway, “Unit Testing Database Code” (www.dallaway.com/acad/dbunit.
html). When people ask us how to test a database with JUnit, we point them first to this article, as it covers the basics and the philosophy very well. Our chapter on testing and JDBC is based in part on the ideas in this article.
Stephen Hall and Simon Monk, “Virtual Mock Objects using AspectJ and JUnit”
(http://www.xprogramming.com/xpmag/virtualMockObjects.htm). The first arti- cle on the subject, as presented in XP Magazine.
Nicholas Lesiecki, “Test flexibly with AspectJ and Mock Objects” (http://www-106.
ibm.com/developerworks/java/library/j-aspectj2/). Another article on the topic of Virtual Mock Objects, presented at IBM DeveloperWorks.
Steve Freeman, “Developing JDBC applications test-first” (http://www.mockobjects.
com/wiki/DevelopingJdbcApplicationsTestFirst). An example of building JDBC cli- ent code using the Mock Object package.
General Testing
Kent Beck, Test-Driven Development: By Example (Addison-Wesley, 2002). This pro- vides an introduction to test-driven development using one longer example in Java and one shorter one in Python. It is a must-read for the JUnit practitioner