Pryce and Freeman’s Growing object-oriented software is a good practical guide to test driven development, as you’d expect from the authors of jMock. It isn’t necessarily easy to adapt piecemeal — introducing end-to-end tests after the fact would require a lot of rewriting and probably introduce more problems than it would be worth assuming you have good partial integration tests, but there is a ton of great information in there. What makes it great is dealing with the hard-to-test pieces like a remote XMPP server and external data types from the very beginning. It’s a welcome break from the tendency in some TDD and BDD writing that seems to assume the world consists entirely of business logic on objects that come from nowhere and disappear after they have had the proper calculation performed on them.
One thing that seems obvious in retrospect is their suggestion to explicitly name your nulls. We’ve all seen tests with such clear code as:
thingifier.broadcast(null, null, amount, null, false);
Naming the nulls can turn this into:
thingifier.broadcast(NO_ACCOUNT, NO_TRANSACTION, account, NO_NOTIFIER, NO_EMAIL);
This is more verbose once you include the definitions, which often means repetition and doing it wrong, naming those nulls makes it much easier for the next person to read the code. As a side effect, the nulls are now typed, so you can’t mix them up with the parameters you care about, and there’s a handy list of all collaborators, used or not, at the top of your test.
I started thinking what if we can take this a step further, because one of the problems I run into with dependencies in tests is I may not know that the Thingifier actually uses the AccountNotifier which I was passing in as null, or conversely, when I go to look at a stack trace, a stack trace pointing me to this line is still ambiguous:
What I’m playing with is using smarter nulls, that is, an object that throws its own NullPointerException with an actual helpful stack trace when any method is called.
There are kinks that haven’t been worked out. The stack trace is useful, but has some extra frames related to the imposteriser. Here’s an example of the test, and the stack trace it generates:
Et voila, a stack trace that tells me which dependencies I assumed were unused that in fact were.
java.lang.NullPointerException: Called invokable#invoke at wlth.experimental.NulleryTest$Invoker.process(NulleryTest.java:22) at wlth.experimental.NulleryTest.shouldThrowNpeWithGoodMessage(NulleryTest.java:31)
The actual code is short, because I delegate all the tricky parts to piece of jMock.
If I’m talking about jMock, you might wonder why I don’t simply mock the type and use a never expectation. It would accomplish exactly the same thing, but it’s a bit misleading, because it appears as though I’m saying “this object must not be used” when in fact all I’m saying is “I don’t think I need this object.”
This is currently sitting in our experimental package. I’m trying it out to see whether it actually makes my tests easier to debug and more readable, and, of course, whether it’s still clear when that code gets to other engineers.
Update: Jared points out that this isn’t really all that much like null anymore and suggests renaming Nullery#unused to Unused#no, so with import static you’d use it like this:
Invoker invoker = new Invoker(no(Invokable.class))
This is more readable, but then I’d lose the catchy title for this post.