Friday, August 8, 2008

assert the machine did not explode

I see it often done by novices, heck, I used to do that myself. When writing a unit test for a method cloning an object, I would put an assertion to check that the original object is left unchanged.

Well, that is wrong. This is checking against a possible bug in the implementation, in which the original object gets modified. But this is just one thing that could go wrong. There are infinitely many possible bugs like this, maybe even one that causes your machine to explode. You can't write assertions against all of them, so don't do it against any of them.

It has two advantages. First is that you stop worrying you forget assertions against some possible bug; that is, if you were worrying about that in the first place. I am sure there are people out there who feel bad about not testing against printing foul language. Second is that the tests themselves become smaller, focused on the positive behavior, therefore easier to read.

In the specific example of cloning: check that the cloned object is equal to the original but they are not the same objects, and that is it. Nothing else.

Unit tests are positive tests, they should only test what you want the code to do, not what it shouldn't do. I am not sure about the case when we fix a defect, but probably a functional test for the defect is enough.

2 comments:

Giles said...

Not sure I agree 100% - you're right that when you write your first UT for a class you should only write positive tests, but I would argue that's just because you don't yet know the likely failure modes for the class. It make sense to write "negative" tests, but only when you're really really sure that the class is likely to fail in the way you're testing for - for example, when you find a defect, it usually makes sense to test for that. Of course, if you can write a "positive" test that checks that the defect is gone by checking some specific bit of functionality, so much the better.

Kamil Dworakowski said...

What I described is the bare minimum that unit tests should cover when
developing in TDD way. I see your point that adding some additional tests,
that although not necessary, are increasing the trust in the system, might
be good. This goes well with the definition of tests as a safety net, if
it can catch some really likely errors people can make, it makes it even safer. But still, I would see it as an exception to the rule.