01 March 2007
"Stop Designing for Testability"
There's an interesting exchange going on in the .Net world (actually, in the Israeli .Net TDD with Mocks world, which I imagine is about as small as a community gets).
First, Roy Osherove wrote that it's occasionally worth breaking OO design principles to make code testable. Then Eli Lopian, supporter of TypeMock, responded by writing that this is a Bad Idea ™.
I'm with Osherove on this one. Although some of the rules he's worried about breaking derive from working with legacy languages, rather than one that really is object-oriented, he makes a fundamental point about Test Driven Development. The effort of adjusting your ideas to make them testable will give you better, more flexible code. This allows you to keep up with the changes that arise in any project and is the other half of the contract you sign when you choose incremental development. I'm sure that Lopian feels that his approach is the way to get there.
I'm not a fan of TypeMock, except where you're trying to crack open a legacy codebase. It's a very smart library but, to me, it misses the point of TDD with Mocks, which is to force the programmer to think about the relationships between the objects they're programming. When you override a feature in a class for the purposes of mocking (or, more often, stubbing), you're addressing a dependency between two objects, but you're leaving it implicit. When I come back to the production class, there's nothing there to tell me that it plays this particular role, so I'll have to do the analysis again. That's why I've never been keen on the cglib versions of the java mock libraries. If it's that simple an object and you don't want to introduce an interface, then don't mock, use the real thing.
But, if you have to mock out an object to get the test to work, then that object is an external dependency and should be passed in. Reaching in to an object and manipulating its internals ties the test to the implementation and makes it brittle—which is the usual objection made against interaction testing. In this case, I have to agree.
One more point, Lopian writes,
Have you ever tried to browse a code with loads of interfaces, it take ages because you have to keep finding the concrete implementation, and the place where the instance was created.In my world that's not the case. First, the usual Java IDEs take me very quickly from interface to implementation. Second, in a codebase that uses Dependency Injection well, significant objects are instantiated in just a few places. The place where the instance is created usually turns out also to be where other objects relevant to my task are also created. Then I know that the design is working.
Correction. Oren Eini is the author of Rhino Mocks,
Labels: design, testability
Cheers!
Roy Osherove
www.ISerializable.com
I guess I should also say that I was just teasing about the "legacy languages" bit. My favourite languages are older still.
So although I agree with this...
Reaching in to an object and manipulating its internals ties the test to the implementation and makes it brittle—which is the usual objection made against interaction testing
..I have also found that TypeMock is useful. We quite often want to mock out domain classes (e.g. Customer/Order). I don't want to implement interfaces or include virtual methods in them just to allow mocking and in those cases I find TypeMock really helps.
On the counter side, I've been working with a team who have some reasonably convoluted domain code. We could have shortcut the process with something like TypeMock, but (to me) the result of breaking things up with interfaces has produced a much clearer design.
It looks like we really needed an extended bake-out, rather than both sides asserting their experience :)
I don't care that the reason is to enable "testability."
Simple as that.
Links to this post:
<< Home
© The authors


