20 January 2008

Just when you thought it was safe to go back in the water...

Rising to the bait again (to keep the fishy metaphor going), there's yet another discussion of how the ability to hack the runtime changes the world. At one level that's true, but not in this case.

Dependency Injection is not a recent invention that a cabal of TDDers forced on the world, and it's absolutely not something that we came up with just so we could crowbar in our Mock Objects.

The relevant design guideline, named the Law of Demeter ("Strong Suggestion" isn't very catchy) was first described by Karl Lieberherr in 1987! The Mock Object pattern came from people applying their substantial O-O experience to TDD in Java, trying to figure out how best to avoid exposing implementation details in unit tests. One of the critical lessons from Demeter is that objects should have explicit dependencies. It helps to keep them focussed on their responsibilities and, as a result, easier to maintain—and a good way to make dependencies explicit is to pass them in.

Unfortunately, since then the world seems to have filled up with DI frameworks which cloud what should be a coding style discussion. This is not about having to configure every last corner of your application in XML, this is about how objects, or small clusters of them, get to talk to each other.

Roy asks, "[...] do you need DI all over the place, or just in specific places where you know dependencies could be a problem?" Well no—if you have enough foresight to know where those places are. I'm struggling at the moment to test against a framework where everything is helpfully packaged up nice and tight so I can't create an instance of one of its core objects. It's actually well written, but the authors just weren't good enough at prophecy to figure out my particular need. That's why I don't rely just on my intuition, I use the needs of my unit tests to help me figure out where the seams should be. To counter the FUD argument, I have absolutely no problem with saying that I don't want tools that do magic because I need guidance with my code.

As Roy (very politely) concludes, there isn't a high enough proportion of really good code in the world (some of it mine) that we should be in hurry to cut back on techniques such as DI. Just because something has been around for a while doesn't mean it's been superseded, especially in as conventional an environment as .Net.

13 January 2008

Avoid mega unit tests

We've just had a posting to the jMock user list that included the following:

I was involved in a project recently where JMock was used quite heavily. Looking back, here's what I found:
  1. The unit tests where at times unreadable (no idea what they were doing).
  2. Some tests classes would reach 500 lines in addition to inheriting an abstract class which also would have up to 500 lines.
  3. Refactoring would lead to massive changes in test code.

I've seen this failure mode on another project I've been helping with, so I think there might be a common pattern.

I don't think any unit test code should get that large, except for unusual circumstances. Unit tests are supposed to focus on at most a few classes and shouldn't need a large amount of set up. What I saw on the other project was enormous amounts of preparation and positioning to get the objects into a state where the expected feature could be exercised. Of course it's hard to understand the point of a test when there's just so much code. If you see this pattern, then I'd suggest that the code (or at least the test code) needs breaking up a bit. On the other hand, integration and acceptance tests that happen to be written using a unit test framework might well be larger.

One thing I need to explore is whether the naive use of interaction-testing is particularly susceptible to this failing, or whether it happens all the time and we're the only ones who get complained to. I am, however, convinced that the emphasis on mainly using Mocks to substitute external systems (some of which I perpetrated myself in the early days) is a deeply bad idea which pushes teams towards the sort of problem described here.