Steve's recent post about the perceived conflict between Testability and Design included this quote from a user of TypeMock:
The key benefit we get from TypeMock is having the ability to fully unit-test the code without impacting the API design. [...] For us, the API is part of the deliverable. We need to make it fairly easy to consume and can't have the architecture of the solution overshadow the usability of the API.
The crux of the issue is in the words "easy to consume". What does that mean? Easy to learn? Or easy to adapt to new, unanticipated situations.
For example, many developers find the java.io API complicated. This is how to open a file for reading:
Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt")));
The equivalents C or Python are much shorter, in Python:
reader = open("input.txt")
The Java version does, however, have a point. Is use of the Decorator and Chain of Responsibility patterns makes it easy to apply in different situations and adapt different underlying transports to the
java.io stream model. In the C approach. different implementations are buried in the runtime, so you have to go to a different mechanism to try anything new.
TDD with mock objects drives an object-oriented design towards one like the java.io API. The design process focuses on discovering common patterns of communication between objects. The end-to-end system behaviour is defined by composing objects instead of writing algorithmic code. That makes code more malleable by experienced programmers but, arguably, makes it harder to learn for newcomers to the codebase or to object-oriented programming itself.
The problem can be addressed by layering expressive APIs that support common operations above the flexible, object-oriented core. A simple API is easy to learn but allows the programmer to drill down to the flexible core when new unexpected situations arise.
JMock itself follows this model. The core is an object-oriented framework for representing and checking expectations. This framework is flexible and extensible. You can create and compose objects to represent and test all sorts of expectations. This level of code, however, is too fine-grained to express the intent of a test. It's like trying to figure out what's for dinner from reading the recipes. That's why we also wrote a high-level API that is closer to the programmer's problem domain. It makes it easy for us to write readable code to set up framework objects and we still have the extension points we need when we need a new feature — and we can test jMock without manipulating bytecodes.