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: ,


Comments:
Thanks for the support, but one small clarification: I'm not the developer of RhinoMocks - that would be Oren Eini (Ayende.com).

Cheers!

Roy Osherove
www.ISerializable.com
 
Oops. Apologies and corrections.

I guess I should also say that I was just teasing about the "legacy languages" bit. My favourite languages are older still.
 
This is atopic I find fascinating. I've kind of given up on designing for testability as I found it complicated my designs in ways that weren't helpful.

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.
 
Thanks for commenting. I'd be curious to see what your domain objects look like, whether it would be simpler just to use the real thing. It would be great if you could send me or post some examples.

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 :)
 
exposing the implementation internals of a class to consumers is BROKEN OOD.

I don't care that the reason is to enable "testability."

Simple as that.
 
As we keep saying, we don't expose internals, we make dependencies explicit.We also find that taking testability into account pushes us towards what we think (IOHO) are better OO designs. Your mileage (whoever you are) may vary.
 
Post a Comment

Links to this post:

Create a Link



<< Home

© The authors