Mock in the middle
The common coding style for testing with mock objects is to:
- Create instances of mock objects
- Set state and expectations in the mock objects
- Invoke domain code with mock objects as parameters
- Verify consistency in the mock objects
While this pattern is very effective for many cases, sometimes the mock object cannot be passed into the object being tested. Instead, that object is designed to either create, look up, or otherwise obtain its collaborator.
For instance, the tested object may need to obtain a reference to an
Enterprise JavaBean (EJB) component or remote object. Or the tested
object may make use of objects with side effects that may not be
desirable in unit testing, like
File objects that delete files.
Common wisdom suggests that this situation provides an opportunity to refactor your object to make it more test-friendly. For instance, you may change the method signature so that the collaborator object is passed in.
In his article "Test flexibly with AspectJ and mock objects," Nicholas Lesiecki points out that refactoring is not always desirable, nor does it always result in code that is cleaner or easier to understand. In many cases, changing the method signature so the collaborator becomes a parameter will result in a confusing, untested snarl of code inside the method's original callers.
The heart of the problem is that the object is obtaining these objects "on the inside." Any solution must apply to all occurrences of this creation code. To solve this problem, Lesiecki uses a lookup aspect or a creation aspect. In this solution, the code that performs the lookup is replaced automatically with code that returns a mock object instead.
Because AspectJ is not an option for some, we offer an alternative approach in this article. Because this is, at root, a refactoring, we will follow the presentation convention established by Martin Fowler in his seminal book, Refactoring: Improving the Design of Existing Code (see Resources). (Our code is based on JUnit, the most popular unit testing framework for Java programming, though it is by no means JUnit-specific.)