Many people think that unit tests are written to catch bugs, however this is only partially right. Unit tests might be a valuable addition to regression-tests and increase a developers productivity, however people might miss the actual benefits of the test-first approach.
Unit testing supports developers in several ways:
- For static typed languages unit testing helps to make the code more robust, since there is a good chance that it catches bugs that the compiler won’t find
- For dynamically typed languages unit tests are the only way to simply verify the correct behaviour of code.
- Unit test are a safety net for refactoring. It’s much easier to change code if you can quickly find out whether this change breaks some critical functionality. Creating more and more tests increase the confidence of doing things right (not breaking something).
- Unit tests are documentation. Good tests are very easy to read and since the code needs to pass all the tests all the time, you can be sure the documentation is up to date. While writing tests won’t somehow magically make the requirements clear, specs are up to date.
- Unit tests help the programmer to concentrate more on an easy to use interface of the class instead of worrying to early about the internals.
- Well-tested code tends to become more extensible, since developers learn to minimize the exposed API of a class (less to test), write smaller classes (easier to test) and create interfaces at the right place (to facilitate testing).
- Writing tests and reflecting on that practice as an integral part of the programmer’s job will make it more likely that programmers will think of unusual failure cases, know what kind of test is most appropriate to exercise each case, and protect the system against them.
- Making TDD right means to realizes that it is not about testing but about defining behaviour. It helps to discover the API of the code under test.
Test first is API design! Unit tests help to find and eliminate inconsistencies in design by capturing the reasoning and circumstances for choosing certain way to implement an feature. While writing unit tests you are creating the first “user” of it. You start the first time to think clearly about a API the class provides. You think about how the class should react to invalid input, invalid order of method invocations and all the other “edge cases”. Therefore unit tests are the first step to formal reasoning about a technical specification.
Then writing Unit tests, I suggest to do the following:
- Think what is the core problem you want to solve.
- Think about the edge cases that might occur. Don’t forget that exceptions are part of the interface! Exceptions don’t necessarily signal something terrible happening, but rather explain the cause for not doing what the object was asked for.
- Model the actual API you want to create (the interface) by using it while writing the test code.
- Code coverage helps to identify areas of the spec that might have been overlooked. 100% Test coverage however is neither a sign that the code is bug free nor working correctly and it might not increase code quality at all. However it might still be useful to identify parts of the code that need your attention.
- Good OO code often focus on interactions between components of the system (it’s behaviour). To test such code, the use of Mocks is essential.
- Not all domains are easy to test (e.g. computer visualization), so keep in mind that unit-tests can’t be applied everywhere. However it might help to think of ways to improve the (manual) test-process.
Behaviour driven development is basically unit testing done right. For more information look at: http://behaviour-driven.org/TDDAdoptionProfile