Monday, September 18, 2006

Mock Objects Revisited

Sven Heyll responded to my difficulties with mock objects and unit tests here:

The purpose of the MOCK

After finishing my JDBC-related code, I spent the last two weeks furiously implementing some higher-level code which did not rely on JDBC, and re-examined my usage of mock objects once again. As Sven indicates in his post, I missed the point the first time around. Sven explains that mock objects are for testing the interactions among objects - for ensuring that the object under test sends the right messages to its collaborators at the right times. I saw the light about a week ago, after reading "Mock Roles, Not Objects", an excellent paper which I suggest to anyone interested in TDD.

In the paper, Steve Freeman and cohorts put forth the view that the use of mock objects should primarily be for the discovery of interfaces within a system. Since TDD defines unit testing as a design activity, the paper contests that mock objects' main purpose is driving design. The paper's recommended approach to coding a module is top-down, starting at the highest level of abstraction:

"This process is similar to traditional Top-Down Development, in which the programmer starts at the highest level of abstraction and proceeds, layer by layer, to fill in the detail. The intention is that each layer of code is written in a coherent terminology, defined in terms of the next level of abstraction."

Mocks thus allow tests for an object to be written in a 'coherent terminology' consisting of messages sent to a set of less-abstract objects which do not exist as of the writing of the test. Following this process, it is argued, produces good OOD:

"TDD with Mock Objects guides interface design by the services that an object requires, not just those it provides. This process results in a system of narrow interfaces each of which defines a role in an interaction between objects, rather than wide interfaces that describe all the features provided by a class."

All of this I have found to be true, after a period of rather intensive coding during which I produced more than one hundred mock-driven tests in the manner the paper describes. It most definitely guided the design of the module I was writing, and kept me from straying into the land of over design.

So what of my troubles with mocks and JDBC? From the abstract of the paper: "[Mock objects] turns out to be less interesting as a technique for isolating tests from third-party libraries than is widely thought." Less interesting indeed. The paper later suggests that a good rule of thumb, especially when just getting started with mocks, is "Never mock a type you don't own." Since the JDBC API's design was not test-driven, it's a real bear to mock properly. EasyMock is also a bit more restrictive in the way it allows for the specification of behavior (though it has been significantly improved since the time 'Mock Roles, not Objects' was written), which has also been a minor source of grief, especially specification of method invocation order. jMock does appear more flexible, and when I get some breathing room I will probably make the switch.