Don't Mock Me

Mock objects allow you to more easily write unit tests for classes with heavy dependencies on other parts of the system. They are a great tool that should exist in the toolbox of anyone doing test-driven development (TDD) or other form of unit testing. Mocks can minimize dependencies on things in your system that make it difficult to test.

Used properly and judiciously, mock objects will help you build a well-designed system with good compartmentalization. Used poorly and rampantly, mock objects can introduce more problems in your system than they purport to solve.

Dependencies

Mock objects are used as a way of breaking dependencies. A dependency exists when one class cannot be built without the existence of another. As an example, a Portfolio class interacts with a StockLookupService; therefore, Portfolio is dependent on StockLookupService. The UML representation:


When writing unit tests for Portfolio, you assume that StockLookupService has its own unit tests. You might implement the unit tests for Portfolio in a class named PortfolioTest, and the unit tests for StockLookupService in StockLookupServiceTest. The unit tests in StockLookupServiceTest, among other things, ensure that StockLookupService makes a remote connection and returns the correct information for a given stock symbol. The unit tests in PortfolioTest don't need to re-prove this functionality.

The unit tests in PortfolioTest do have to prove that, when calculating the value of a portfolio containing Vitria (VITR) stock, the String “VITR” gets sent to the StockLookupService. They also must prove that the value retrieved is added to the total Portfolio value. In short, PortfolioTest must prove that Portfolio interfaces correctly with StockLookupService. It doesn't have to prove the implementation details of StockLookupService itself.

If you can always depend on StockLookupService being available and fast, you can code PortfolioTest to have Portfolio work directly with the live StockLookupService. However, this dependency may cause you some problems in testing. The next section discusses some of these problems.

Bad Dependencies

If StockLookupService communicates externally to get its information, at least three problems might arise when executing the tests in PortfolioTest:

Tests take considerably longer to run:

Connecting to an external resource (such as a web service or database) and retrieving information can take hundreds of milliseconds per event, a seemingly trivial amount. But if hundreds or thousands of unit tests must do the same, the time to execute the entire suite of tests may rise dramatically. Instead of running in a reasonable handful of seconds, unit tests may take several minutes or longer to execute.

Most classes in data-centric business systems directly or indirectly need access to a database, meaning that there are hundreds of tests that result in a connection being established. Tests that directly work with the database must still be run, but hopefully there aren't many. The other tests might be candidates for using mocks.

Tests don't run consistently:

I worked on a system that communicated with a server located within our facilities. Tests would fail once or twice a day due to a server crash. I would get up, walk upstairs, find someone with badge access to the server room, get in, ensure the server rebooted, and return my desk. A half hour blown!

Another source of inconsistency in test execution is data volatility. If you try to test Portfolio with the live StockLookupService, you may get a different value for a stock each time you make a lookup. The tests for Portfolio will be far more difficult to write.

Tests work best if they completely control any data involved. By having your tests generate and work with their own data, you maintain isolation from any nondeterministic external factors that can corrupt the reliability of your tests.

Tests don't run at all:

Ever try to develop an enterprise system on a disconnected laptop? There are usually such tight dependencies to databases and external APIs that it is near impossible to work in a standalone test mode. You might be developing a class with no direct interaction with the database, yet due to bad dependencies you can't run its tests.

Or, you need access to that upstairs service, except the server, which just crashed, is in Cedar Rapids, Iowa, and the Cedar Rapids support staff has just left for the day. Tough luck.

Several other situations may prevent you from running tests. A few examples:

Building the Portfolio Example

I will use the portfolio example in the remainder of this article to demostrate how you can eliminate bad dependencies through the use of mocks.

First write a test that ensures the value of an empty portfolio is 0. Then write a test that adds a stock holding with one share to the portfolio, and ensure that the total portfolio value matches the single stock value.

public class PortfolioTest extends junit.framework.TestCase {
	public void testCreate() {
		Portfolio portfolio = new Portfolio();
		assertEquals(0, portfolio.currentValue());
	}

	public void testSingleHoldingSingleShare() {
		final int numberOfShares = 1;
		final String stockSymbol = "VITR";
		Holding holding = new Holding(stockSymbol, numberOfShares);

		Portfolio portfolio = new Portfolio();
		portfolio.add(holding);
		assertEquals(???, portfolio.currentValue());
	}
}

The problem is, what is VITR currently valued at? Its value could change each time you ask. To make the test work, you must somehow “fix” the value returned by the stock lookup on VITR. For now, plug in an arbitrary value for the ??? in the code in order to pass compilation (and fail the test):

assertEquals(-1, portfolio.currentValue());

The code for Portfolio itself1:

public class Portfolio {
	private Holding holding;

	public int currentValue() {
		if (holding == null) return 0;
		StockLookupService service =
			new StockLookupService();
		return
			service.lookupStockValue(
				holding.getSymbol()) *
				holding.getNumberOfShares();
	}

	public void add(Holding holding) {
		this.holding = holding;
	}
}

The currentValue method instantiates StockLookupService. The sole holding's stock symbol is used to retrieve a value via the service. This value is multiplied by the number of shares for the holding, and the result is returned to the caller.

Mocking the Target of Dependency

You only want to work with PortfolioTest and Portfolio, but you're dependent upon StockLookupService and the value it returns. To solve the problem, you can create a mock implementation of StockLookupService that implements the same interface.

There are several ways to create and use a mock implementation. This article discusses hand-coded techniques only. There are tools available to assist you in building mocks. The code produced by these tools still must be used in the context of one of the three mock variants described here.

The examples in this article use an interface type, StockLookupServiceIF, to represent the common methods that must be implemented by StockLookupService and the mock.

public interface StockLookupServiceIF {
   int lookupStockValue(String symbol);
}

Parameterized Mocking

The most direct way2 to implement mocks is to have the Portfolio constructor take and store a reference to an object of type StockLookupServiceIF. Production code that instantiates Portfolio would pass a StockLookupService to its constructor; PortfolioTest instead passes it an instance of the mock StockLookupService.

public class PortfolioTest extends junit.framework.TestCase {
	// ...
	public void testSingleHoldingSingleShare() {
		final int numberOfShares = 1;
		final String vitriaSymbol = "VITR";
		final int vitriaValue = 5099;

		Holding holding = new Holding(vitriaSymbol, numberOfShares);

		StockLookupServiceIF service = new StockLookupServiceIF() {
			public int lookupStockValue(String symbol) {
				if (symbol.equals(vitriaSymbol))
					return vitriaValue;
				return 0;
			}
		};

		Portfolio portfolio = new Portfolio(service);
		portfolio.add(holding);
		assertEquals(vitriaValue, portfolio.currentValue());
	}
}

This implementation uses an anonymous inner class to define the mock, which provides everything you need to know about the test and mock in one place. The mock could have been defined as an inner class or as a separate top-level class.

The Portfolio code knows nothing of mocks. It just knows how to send the lookupStockValue message by using an interface:

public class Portfolio {
	private Holding holding;
	private StockLookupServiceIF service;

	public Portfolio(StockLookupServiceIF service) {
		this.service = service;
	}

	public int currentValue() {
		if (holding == null) return 0;
		return
			service.lookupStockValue(
				holding.getSymbol()) *
				holding.getNumberOfShares();
	}

	public void add(Holding holding) {
		this.holding = holding;
	}
}

That's all there is to implementing a simple mock.

Problems with Parameterized Mocking

In the stock portfolio application, another class constructs instances of Portfolio. That class also creates or obtains an instance of the production class StockLookupService. The following code might appear somewhere in a class called PortfolioApplication:

StockLookupService service = new StockLookupService();
Portfolio portfolio = new Portfolio(service);

Suppose Portfolio is the only class in the system that must interact with the stock lookup service. Instead of Portfolio being the class directly dependent upon StockLookupService, PortfolioApplication becomes dependent upon it:


The StockLookupService was previously an encapsulated implementation detail of Portfolio, but it is now unnecessarily exposed to PortfolioApplication! The PortfolioApplication class shouldn't have to know that the StockLookupService even exists.

Note that externalizing knowledge of StockLookupService doesn't immediately introduce a new dependency. Dependencies are transitive: PortfolioApplication was dependent upon Portfolio, and Portfolio was dependent upon StockLookupService, so by definition PortfolioApplication was already dependent upon StockLookupService.

If Portfolio itself is accessed by the application via an interface, it is decoupled from the application. While this is a contrived circumstance for the portfolio example, it can happen and illustrates the problem with moving the dependency. The following diagram shows the relationship before introducing the mock:




The need for PortfolioApplication to instantiate StockLookupService introduces an unwanted dependency. After introducing the mock:


Before, PortfolioApplication interacted with an abstract interface to what could be construed as a modular portfolio subsystem. Now, two previously buried pieces of that subsystem must be exposed—StockLookupServiceIF and StockLookupService.

Another problem with parameterized mocking: once you start passing things around, sooner or later things get passed down through multiple layers of objects. This exacerbates the whole dependency issue and clutters your classes.

Factory-Based Mocking

A factory can eliminate the need for PortfolioApplication to construct StockLookupService instances. The class ServiceFactory normally returns a production StockLookupService object, but is overridden by the test to return a mock instance:

public class ServiceFactory {
	private static StockLookupServiceIF instance = null;
	public static StockLookupServiceIF create() {
		if (instance != null)
			return instance;
		return new StockLookupService();
	}
	public static void setInstance(StockLookupServiceIF mock) {
		instance = mock;
	}
	public static void resetInstance() {
		instance = null;
	}
}

The test becomes responsible for setting the mock into the factory:

public void testSingleHoldingSingleShare() {
	// ... setup code ...
	ServiceFactory.setInstance(mockService);
	try {
		Portfolio portfolio = new Portfolio();
		portfolio.add(holding);
		assertEquals(vitriaValue, portfolio.currentValue());
	}
	finally {
		ServiceFactory.resetInstance();
	}
}

The test also ensures that the mock instance is blown out of the factory, otherwise other tests will unwittingly use it. For a single test, a try/finally block ensures the factory is reset.

Finally, Portfolio uses the create method of ServiceFactory to obtain its service:

public class Portfolio {
	// ...
	private StockLookupServiceIF service = ServiceFactory.create();

	public int currentValue() {
		if (holding == null) return 0;
		return
			service.lookupStockValue(
				holding.getSymbol()) *
				holding.getNumberOfShares();
	}
	// ...
}

Portfolio becomes the only production class that must recognize ServiceFactory. PortfolioApplication is oblivious to both ServiceFactory and StockLookupService.


Problems With Factory-Based Mocking

You must use try/finally blocks (or use a setup method) to reset the factory for each test that sets a mock instance into the factory. Using try/finally blocks clutters the code; more importantly, it is an easy thing to forget. If one test forgets, you have an insidious problem that is difficult to decipher when you have thousands of tests.

You can also use a system property or other external switch to designate when mocks are required. This works well if all tests use the mock, but as soon as you have one test needing the production class and not the mock, you are back to the previous problem of remembering to reset the switch.

Factory-based mocks introduce an additional layer of overhead and complexity in the production code. While it's OK to occasionally expose object state for testing purposes, I question introducing a factory solely for the purpose of testing.

Deferred Factory Method Mocking3

A deferred factory method supersedes a factory method available within the class of the object under test.

public void testSingleHoldingSingleShare() {
	// ... setup code ...
	Portfolio portfolio = new Portfolio() {
		StockLookupServiceIF getService() {
			return mockService;
		}
	};
	portfolio.add(holding);
	assertEquals(vitriaValue, portfolio.currentValue());
}

The implementation of getService in Portfolio will return an instance of the production class StockLookupService. The test above supersedes this getService definition, instead returning a mock service instance. Code in Portfolio:

public class Portfolio {
	// ...
	public int currentValue() {
		if (holding == null) return 0;
		return
			getService().lookupStockValue(
				holding.getSymbol()) *
				holding.getNumberOfShares();
	}

	StockLookupServiceIF getService() {
		return new StockLookupService();
	}
	// ...
}

Note that the method getService has package-level access. Many shops put their test classes in the same package as the production classes they test.

The UML for this solution:


A riskier variation exposes a service instance variable and requires the test to set its value to the mock. While this solution involves neither factory methods nor subclassing, it still results in the same class structure. Michael Feathers calls this pattern Supersede Instance Variable.

Problems With Deferred Factory Method Mocking

A goal in building object-oriented systems is to maintain encapsulation. The more clients send messages to objects through their public interface, the more clients are decoupled from those objects. The more implementation details clients know about the objects they interact with, the tighter the coupling in your system and the poorer the system design.

Should tests know implementation details of the classes they test? In the above example, getService is not and should not be publicly accessible. But is only exposing it to package level still too much?

If you do TDD as intended, you write tests against the functionality of classes. You test Portfolio as if the test were a client of it. The test sends messages to a Portfolio through its public interface, then makes assertions to ensure all post-conditions are met. These assertions are usually made through the public interface of the class. Occasionally you will need to make a method package that was originally private, in order to introspect the object. Occasionally, you may want to implement deferred factory method mocking using an otherwise private method.

Once your test accesses these non-public methods, it becomes tightly bound to the production class implementation, and vice versa. This might not be a problem, but taken to the extreme, refactoring becomes extremely difficult. You can't change the implementation of the production class—the things that should be private per encapsulation goals—without breaking tests.

Deferred factory method mocking isolates this tight coupling to a single factory method. You concede to a slightly less-than-ideal design in this case, since the likelihood that the getService factory method needs to change is minimal.

Used in an isolated and minimal fashion, deferred factory method mocking is very effective. When it is abused, you get an extremely rigid system. Anything preventing the ability to refactor at will is bad.

In Closing

There are many additional variant mock implementations, but each can be placed in one of the three design categories: parameterized mocking, factory-based mocking, or deferred factory method mocking.

There is no “perfect” way to implement mocks. Each way carries with it some design issues. None results in a “perfect” design. This should suggest that while mocks can be useful in solving bad dependency problems, they introduce other issues into your system that might be a cause for concern.

Mocks can degrade confidence in the system. By definition, when you test with mocks, you are not testing with “the real thing.” If you misunderstand the target that you are mocking, there is a real possibility that you have overlooked something that will introduce a defect in your system. You must adequately capture all the test variants based on the target's possible behaviors. For example, what happens if the target throws an exception? Tests for Portfolio must handle this and other circumstances caused by StockLookupService.

To help restore lost confidence, continually execute acceptance-level tests that exercise the system from a “live” user standpoint. The acceptance tests work against production code, not mocks.

Mocking is still extremely valuable. In many cases the slightly imperfect design is a vast improvement over the inability to test. The rule of thumb for mocking is, don't... unless you must. Ensure you have a valid reason for mocking. The reason will normally be one of the three bad dependencies I discussed.

1No collection and associated iterator?!! Don't fret. Per TDD, you write just enough code to meet the current test's specs. You'll very soon write another test that deals with multiple holdings.

2See the seminal article, “Endo-Testing: Unit Testing Using Mock Objects,” by Tim Mackinnon et. al.

3Michael Feathers uses this term in his upcoming Prentice Hall book on legacy code.