Unit testing done right – Better then sliced bread

Maybe the biggest thing since the rise of object oriented programming is test driven development. It’s also as hard to “get”. To help you on your journey, here some tips:

  • Have patience. Good unit testing need to be learned (like OOP, also see “TDD, What a bitch”). Use code-reviews of good unit-tests to learn good practices.
  • Again, have patience. Don’t skip unit-testing after some time. If you need to change tests often try to find the reason why and solve it. In many cases it’s actually a design issue (see next point). Everything falls with cleanly separated (and therefore testable) layers and DRY code. The need to reduce the dependencies for testing using mocks/stubs. “Duplicate code always represents a missing abstraction.” Robert C. Martin in Clean Code Tips
  • Group tests by the user-story they are testing. This ensures that you test the right thing and everything that belongs together is in one file. Use user-stories to define what feature is needed. Use BDD-Specs to describe the details of how a feature should behave. A test is simply a specification translated to code.
  • Tests should not depend on each other. Each test should be able to run correctly without any other test to be executed beforehand.
  • Tests need to be short (no scrolling). A better name for unit tests might be microtests. They just check one thing per test case. This helps to precisely identify where the bug is located (no need for debugging). It also helps to avoid brittle tests ( = tests that need to change often). If tests are too long, there is a good chance that the code under test has too many responsibilities and need to be split.
  • Run microtests as often as possible (best after each compile). Therefore make tests fast. Don’t test the database if avoidable. Don’t test more then one component at a time. More complicated, longer running tests (usually integration tests) can be run over night using an continuous integration server.
  • Microtests can be used to speed up implementation (“Need for speed”). They are a replacement for starting the whole system to check out whether the implementation works correctly. No monkey-testing no more!
  • While writing tests, think of everything that might get wrong (e.g. test whether code throws exception on invalid input, …). Many people tend check only whether the code behave well then it gets correct input. However code should always behave correctly (e.g. not crash) independent on the input it gets. This helps to write more robust code (again less debugging).
  • Write tests before writing the code. It helps to be more clear about the requirements and help to focus the programming efforts. In software development typing is never the bottleneck. Thinking is. Tests make development faster because they help to focus: “How TDD and Pairing Increase Production” and “They’re Called Microtests”
  • Let the tests drive the design of your API. Also see “Unit tests: An API design tool

More on this topic:

Unit tests: An API design tool

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:

  1. 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
  2. For dynamically typed languages unit tests are the only way to simply verify the correct behaviour of code.
  3. 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).
  4. 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.
  5. 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.
  6. 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).
  7. 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.
  8. 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:

  1. Think what is the core problem you want to solve.
  2. 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.
  3. Model the actual API you want to create (the interface) by using it while writing the test code.
  4. 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.
  5. 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.
  6. 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