Tuesday, 15 March 2011

Unit Tests and Specifications

I came across this the other day whilst watching a Google Tech Talk on youtube. The lecturer, Misko Hevery, was talking about writing testable code – something in which I’ve a great interest and, half way through his lecture, he started talking about specifications. At first this seemed to confuse half the audience in that they seemed to assume that the lecturer was talking about artifacts such as Requirements, or Functional Design specifications which were the sort of thing that was usually written in Word before the advent of UML (or Booch / OMT if you’re that old). What he was actually talking about was the formalisation of unit tests using a scenario based naming framework.

What do I mean be this? Well I’ll pinch his example to demonstrate… Suppose you have an object called MailReader and it has a method named:
public void sync(Pop3 pop3). 
If I was coding this, my usual tack would be to write a bit of skeletal code and then to write my first test, probably named:

@Test public void testSync() {};

I would run the test and figure out that my method roughly worked in the ‘happy flow’ scenario. I would then look at all the possible input values, edge cases and error flows writing a simple test for each one. As all the tests are very similar to each other, I’d probably use a bit of cut ‘n’ paste Cargo Cult coding and end up with a whole set of similarly named unit tests:
@Test public void testSync1() {};
@Test public void testSync2() {};
@Test public void testSync3() {};
@Test public void testSync20() {};
This method works for me: all test scenarios are covered and I’m pretty certain that my code works.

The downside of this method is that you end up with a bunch of working tests, whose method names do not convey any information about what’s going on.

Now, the lecturer was basically proposing that each test method name related the particular scenario it’s testing, so instead of testSync() and testSync1() etc. we write something like:
@Test public void testSyncMailboxContainsMessages() {}
@Test public void testSyncMailboxIsEmpty() {}
@Test public void testSyncMailboxContainsOneMessage() {}
@Test public void testSyncExceptionIsThrown() {}
The point being is that I’d end up with the same number of tests as my old cut ‘n’ paste method doing the same jobs, but my code would be clearer and more maintainable, especially by other programmers, as each test method would cover precisely one scenario and there would be no confusion about what that scenario was.

There are a couple of things I don’t like about it (and I’m being very picky here): firstly although the name specifications covers what we are doing, it is confusing (although I can’t think of a better name) and secondly, you will have a Unit test that contains a bunch of very long method names, which to me looks ugly (although testSync5() looks uglier).

So, have I got this right, or have I missed the point. Feel free to let me know.

No comments: