Unit tests should not test software
Register your claim
When I refactor or make changes to code I need somehow to verify that the changes do not break anything. I want to do it with unit tests. Unit tests are my safety net. My philosophy on unit testing is something like, if you do not want me to change some of your functionality then “register your claim” on that functionality as a unit test.
Unit tests specify
There is a lot of functionality (or business logic) that is accidental, meaning that it is not really necessary and it might even be plain wrong, a bug. It happens, a lot. It is hard to know if a piece of code is accidental or not if you do not have a way to express it. A unit test is a great way to express your intent and your “real” functionality. For me, unit tests are specifications. They specify the intended functionality. This is explicit functionality. I love explicit functionality. In fact I love almost anything explicit when it comes to code. I want to read loud and clear what the intentions are. The opposite of explicit functionality is of course implicit functionality. This is the functionality that resides in the code without anything or one to claim its importance. Implicit functionality might always be accidental, and that is why it is so hard to work with that kind of functionality. You do not know if it’s supposed to be there.
Unit tests verify
If all important functionality is ensured with unit tests, you have your safety net. The unit tests verify what you specified. Then refactoring is a dream. Keeping the codebase neat, up to date and clean is easy. To be able to nurture your codebase, through refactoring, you will need this safety net. It verifies your intentions and ignores all unnecessary code.
Unit tests do not test software
This way unit tests ensures that the intended functionality in your classes stays there, and makes it easier to refactor. But it does not check if your system work. It does not verify that you have put it all together the right way. It does not check if everything goes right in the databases, on files or in the user interface. Partly integration tests will do this job for you, and partly manual testing.
Abstractions through OO
When I talk about unit tests, I consider my units to be classes. I also want to stress the point of knowing where to put your logic. Responsibilities should be placed where it belongs, in the right concepts (on the right classes). This is the fundament of object orientation, expressed elegantly through domain driven design. The concepts are the classes. Thus it is very important to verify the logic where it belongs, on the classes. And why should we use classes and separate logic? There are mighty reasons for this, but for now let me just say: to avoid cognitive overload.
You need all types of tests
So, unit tests are there to specify the behavior of the systems concepts (think DDD). This is imperative to be able to refactor the core of your system. But as I’ve said, this is far from enough to test your system. I find integration tests best for testing your software. Tests that are more like acceptance tests, checking a complete path of calls from top to bottom (and yes, I often include the database in these tests). In addition there are things you will not be able to test automatically (that easily). How does the gui look and feel? You need someone to test that too.
To conclude, you need a whole lot of unit tests to lock down functionality. You need integration tests to test your software, and you need people to test gui and other stuff that isn’t feasible to test any other way. And my opinion is that you should do all this before you send it to QA.
-Tore Vestues

