Who cares how long the test is?! August 8, 2007
I was speaking with someone at BarCamp and they suggested that your unit test
should never be longer than the thing it’s testing. His thinking was that if
your test is longer than what it’s testing then your tests will eventually need
a test framework to make sure they’re correct. This was of course one of his
excuses for not writing tests at all. And, superficially at least, there is some
validity to his argument, but it’s based on a flawed premise. It’s based on the
premise that the code you write in your test suite is complex enough that it too
could be brittle, and if you ever find this happening it’s a sure sign that
you’re doing in wrong. Let me explain:
Unit Tests should be simple… really
simple. Each test routine should basically have these parts:
- An instance of the thing you’re testing.
-
If it requires parameters in it’s constructor or in the method you’re
testing you create some
mock
objects (NOT STUBS) to pass it. -
A series of assertions to test that what you thought would happen did
happen.
Instantiating an object is not a complex operation. It’s usually something along
the lines of
Bar myBar = new Bar();
You don’t have to worry about that breaking, normally because
constructors don’t tend to do much. If your constructor does have any complexity
happening under the covers then you’ve got a unit test for it somewhere else and
thus you don’t have to worry about it.
Creating a mock object not only avoids potential complexity but utilizes a well
tested library so you don’t need to test that either. Yeah it may take a few
lines but it isn’t complex or brittle and it is guaranteed to work exactly as
you tell it to.
Assertions are just
assertEquals(”camels”, myBar.favoriteAnimal());
// or maybe…
assertTrue(myBar.isSimplistic());
First off, the assertions themselves are methods from a unit testing suite, you
can rest assured that they not only work correctly but have full unit test
coverage. So you don’t have to worry about them. Second, they’re brain dead
simple. There’s no logic, so there’s nothing to break. The only thing here with
the potential to blow up is the methods you’re testing and that’s why you wrote
the test in the first place.
Your unit test should have absolutely no business logic in it. If it does then
you probably need to refactor your code.
So, lets look at a simple concrete example of why the length of your method is
irrelevant to the length of your test.
Most of you are familiar with QuickSort. It’s a simple and fast method to sort a
list of things. You can probably write a QuickSort algorithm in under 15 lines
of code. So what happens if you pass your quicksort algorithm a list of twenty
shuffled items. You’d want to test that it correctly sorted all twenty of them
wouldn’t you? Considering that this is a test you know exactly what the input is
and exactly what the output should be, but there’s only one way to really test
it. You have to check every slot of the returned list and make sure it’s got the
correct item in it. So that’s at least one line to set up the list you’re going
to pass to your quicksort method, one line to pass it in, and twenty lines to
test the results. Something like this:
SortingUtilities sortUtils = new SortUtils();
String[] unsortedMen = new String[]{”tom”, “dick”, “Harry”……and 17 more};
String[] sortedMen = sortUtils.myQuickSortMethod(unsortedMen);
assertEquals(”dick”, sortedMen[0]);
assertEquals(”Harry”, sortedMen[1]); // case insensitivity testing too
assertEquals(”tom”, sortedMen[2]);
// and so on for another 16 lines
Grand total of 23 lines plus 2 more to start and end the test method itself,
plus you’d probably want to break that list of unsortedMen down into multiple
lines so it would be readable. All that just to test a fifteen line method. For
good measure you might want to add a line to test that the size of the array
that came out is the same as the size of the array that went in, just to make
sure that nothing was accidentally duplicated in there. But nothing,
nothing, in that list even remotely needs
to be tested itself because it’s all brain dead simple. There’s nothing complex.
There’s no business logic, there’s nothing you have to worry about. The fact
that it happens to have more lines than the method it’s testing is totally
irrelevant.
The only excuse I’ll hear for not wanting to do this is that you’re too damn
lazy. At least that’s honest. But claiming that the length of your test suite
has any relevance to the length of the method under test is just plain ignorant.
You’re a programmer. Use your brain. Don’t make poorly thought out, half-assed
excuses not to do the right thing. If you’re going to avoid doing the right
thing at least be honest about why you’re doing it. Laziness sucks, but at least
it’s honest, and since people don’t generally like thinking of themselves as
lazy it has the potential to kick your ass into gear. And, because it always
comes up, the excuse that writing all those lines of test code “slows you down”
or “takes too long” is bullshit because we all know that the vast majority of
time spent on any project is in debugging and maintenance and when you’ve got
good tests that time is dramatically
reduced. You end up saving far more time than you spent writing your tests, and
your end result is just more bug free and stable. If typing out twenty lines of
brain dead assertions takes you more than a minute and a half you need to get
off your butt and take a typing course.
Leave a Reply