JaVa
   

Mock Objects from the Assembly Line

The fact that mock objects are a standard technique of the test-first approach has led to the emergence of some very interesting software products, which simplify or avoid mock object creation. A look at these freely available libraries and tools is recommended to all who use mock objects more than sporadically.

Mock Library

Mackinnon [00] mentions a mock object library, which is now freely available [URL:MockObjects]. It can relieve us from a lot of implementation effort. The basic principle consists in the use of so-called expectation classes. These expectation classes encapsulate the expected behavior, the actual behavior, and a balance between the two aspects in verify(). To better explain this idea, we will implement the above MockLogger class by using the library:

import com.mockobjects.*;
public class MockLogger2 extends MockObject implements Logger {
 private ExpectationCounter closeCalls =
 new ExpectationCounter("MockLogger.close");
 private ExpectationList logLines =
 new ExpectationList("MockLogger.logLine");
 public void addExpectedLine(String logString) {
 logLines.addExpected(logString);
 }
 public void close() {
 closeCalls.inc();
 }
 public void logLine(String logLine) {
 junit.framework.Assert.assertNotNull(logLine);
 logLines.addActual(logLine);
 }
 public void setCloseExpected() {
 closeCalls.setExpected(1);
 }
}


The resulting implementation, MockLogger2, has shrunk by almost half, compared to MockLogger, and it is better readable, once you get used to working with expectation objects. If you derive your own mock class from com.mockobjects.MockObject, you can normally do without implementing the verify() method, because all expectation objects declared in instance variables will be verified automatically.

In addition to a basic set of expectation classes, the library offers ready-made mock classes, including classes to test servlets and JDBC invocations (see also , ).

Mock Generators

The idea to generate mock classes based on the interfaces they implement suggests itself. I am aware of two approaches are known to the author for this purpose:

Mock Objects the Easy Way

Driven by the tiresome effort involved in mock class implementation, the EasyMocks-Library [URL:EasyMock] pursues a new idea. EasyMock is a class library that provides an easy way to use mock objects for given interfaces. This means that we don't have to write interfaces and mock implementations for all kinds of uses. Instead a mock implementation of a given Java interface can be created and controlled at runtime. To see how this works, we use an example that implements part of the LogServerTest class:

import org.easymock.*;
public class LogServerTestUsingEasyMock extends TestCase {
 private LogServer logServer;
 private MockControl control;
 private Logger logger;
 public LogServerTestUsingEasyMock(String name) {...}
 protected void setUp() {
 control = EasyMock.strictMockControlFor(Logger.class);
 logger = (Logger) control.getMock();
 logServer = new LogServer(logger);
 }
 public void testSimpleLogging() {
 logger.logLine("(0): first line");
 logger.logLine("(1): second line");
 logger.logLine("(2): third line");
 control.activate();
 logServer.log(0, "first line");
 logServer.log(1, "second line");
 logServer.log("third line");
 control.verify();
 }
}


First, we need an additional MockControl object that rids us from having to create and eventually verify a Logger instance; we will achieve all ofthis with EasyMock.strictMockControlFor(Class aClass). This detour is necessary because the underlying Java proxy mechanism does not allow us to extend the "simulated" interface. The actual test is a kind of capture and replay mechanism. First, we record the desired behavior—the three lines logger.logLine(...). Next, we activate the mock functionality, run the code under test, and use the final verify() call to verify whether or not the recorded behavior was actually executed. We opted for the "strict" variant of the MockControl object so that the invocation order is included in the verification part. In addition to what the preceding example demonstrated, EasyMocks allow the specification of return values and exception throwing. This saves us from having to implement a dedicated mock class. However, we won this shortcut at the cost of poorer readability of the test code. On the upside, EasyMocks are more stable towards changes to the interface of a class.

It is interesting to ask whether consequent refactoring of the mock code created in this way would eventually take us back to standard mock classes. We are curious to see whether this approach will become firmly established in the test-first community.


JaVa
   
Comments