Last week I was on the TDD Geecon conference in Poznan, Poland. It took place in a cinema. What a cool place for a conference, PowerPoint presentations on a big cinema screen. My overall impression is that testing and continuous integration have become mainstream and common practice now among developers. TDD is just an aspect, a special emphasis on a test first approach. For an overview about TDD I recommend Steve Freeman‘s presentation Ten Years of TDD and his book “Growing Object-Oriented Software, guided by Tests”.
I observed earlier that testing is like cleaning: as a cook you can cook in a dirty kitchen, but in the long run it is not recommendable to let bugs grow in the kitchen. Similarly as a developer you can develop applications without tests, but it is not recommendable to have undetected bugs in the application. And if you always clean a bit, that is to say if you always check in your code a little bit cleaner than when you checked it out, you will eventually arrive at a clean system (like a liveness property in distributed computing).
Tests are like seat belts. There was a time when we drove without seat belts and had no insurance at all. Today it is everywhere required to use them during driving. They increase security and guarantee that nothing bad happens. Just as in real crash tests, we try to break the system in tests in every possible way. The continuous execution of tests is the task of CI server, which rely on a good test coverage. They are like insurances, possibly costly and cumbersome to configure, but once they are setup, they are increse safety a lot and a very comfortable.
If tests are like seat belts, then mocks can be compared to crash test dummies: they look and act a bit like the real objects, but they are just fake. And they are quite useful, if they are not overused. Overuse can be costly and bad. It is important to use safety tools like tests and dummy objects in the right way. A few points I noticed in the conference were:
- TDD guarantees that your code has tests. Because you write tests first and code later, the code you write is always covered by tests, and can be refactored well later on.
- Mocks are indispensable for TDD, like crash test dummies for car safety, but bad if you overuse them. Do not overuse mocking.
- A single test should only test one thing, one rule, or one piece of business logic. If the test fails, you know exactly which rule has been violated
- Tests should reflect what a method does, not how it does it. We should test behavior, not the underlying algorithm or implementation. If you test the implementation, you can not change it without breaking the test.
- Sometimes duplication can be useful to avoid duplication, duplication of test cases is useful to eliminate duplication in code if it helps to remove repetition by suitable refactoring. In the end more repetition in tests could mean less repetition in code.
If we take a look back, then we notice that tests have always been import in engineering. In rocket science it is well know that a rocket which looks perfect on paper but has not been tested is more likely to end in explosion than in orbit. For the moon rocket Saturn V for instance, each element was tested individually over and over again before the first launch of a complete rocket: the rocket engines, the different stages, the Launch Escape System, etc. I read somewhere that there was not single element of the Saturn V rocket which has not been tested before thoroughly. Tests can bring us to the moon and back :-)
Tests are also a bit like exams. They examine if the system or the code fulfills all the necessary requirements. In the far future, when we have possibly deep learning in autonomous entities, we might write only tests, and the system tries to pass the tests after it has learned and trained for a while itself. You write the test and the system does the rest. In this sense, TDD could be the future.