Earlier this week I spent three days with Robert C. Martin, also known as Uncle Bob. He was in Stockholm giving a course on Advanced TDD, which I had the privilege to attend to. Hearing Uncle Bob talk about TDD for three whole days lead to quite a few thoughts and insights. This post is an attempt to summarize the main points I took away from his course.
Don't try to convince others, convince yourself
As a test driven developer on a team with non-test driven colleagues you have probably asked yourself the question "how do I convince the others to take up TDD?". The answer to this question is that you probably can't convince anyone to start doing TDD. That decision has to come from each and everyone themselves. What you can do is to make sure that you are really convinced of the benefits of TDD, yourself. If you are, it will shine through. As time passes the quality and effectiveness of your work will hopefully speak for itself, and help inspire the other developers.
By focusing on following test driven practices yourself and being open for sharing your point of view when anyone shows interest you can slowly but surely spread the knowledge.
Tests are truly first class citizens
Tests are important. Very important. The test code deserves the same tender love and care as your production code. Fore some reason test code is generally not held to the same standards as the production code. You should strive to keep your test code just as DRY and expressive as all other code. Make sure not to repeat tedious setup code or hard-to-read asserts. Use extract method if you can, and split the tests to make sure they are only testing one thing.
The test is more important than the prettiness of your public interfaces. The consequence of this is that it is better to add test specific methods/properties to your objects and to
increase the visibility of private/protected methods than to let your tests suffer. However, every time you are tempted to promote a private method to public for testability you should stop and think if it is not really an inner class that is trying to escape.
For some reason, test code is generally not shipped with the production code. Why not use the fact that you have an extremely powerful diagnostic tool available and ship the tests when you release, together with some light weight test runner software. By doing that you can drastically improve your ability to diagnose problems that occur on the systems on which your software is being deployed.
Make sure you can trust your test suite
Once you have worked on a code base that is covered by a test suite you trust, no matter the size, you tend to get hooked on the feeling of freedom to refactor and change the code. When you reach this point of trust you can start reaping some of the most powerful benefits of a test driven code base. The flexibility of the code allows you to do whatever you please and get immediate feedback. Your QA process is drastically shortened which makes frequent releases and deployment a breeze.
Achieving this kind of trust for your test suite is not something you do easily. It takes hard work and discipline. A good way to do keep the trust level high is to follow the three laws of TDD (see "TDD is hard, get used to it" below).
Don't ask for permission, just do it
A colleague of mine has the philosophy that it is easier to ask for forgiveness than for permission. This is especially true when it comes to TDD. If you start asking management and colleagues if you should do TDD or not, in an organization that is not that progressive, you will probably hear the usual anti-TDD arguments. If you don't want to take the fight, the easies way to resolve this issue is to not ask in the first place.
Most likely, neither colleagues nor management will explicitly tell you NOT to do TDD if you just do it as a natural part of your development process. If your team/management is extreme enough to not want test code in the code base, or something crazy like that, just keep the test code locally on your machine instead of checking it in, or simply throw it away after letting it drive your production code.
Tests are both documentation and functional spec
By letting acceptance tests and unit tests drive the implementation of requirements, as well as the modifications made due to changed requirements, your tests always reflect the current functional spec. Your acceptance test provide an executable functional specification. If you are using a tool such as FitNesse that specification will be easily understood by the customer. In the best of worlds, this specification is not only easily understood by the customer, the customer is also able to create and update specs. Even if that is not the case, the acceptance tests provide a powerful basis from which customer and developer can cooperate to evolve the spec.
The unit tests provide an extensive documentation of the actual implementation of the requirements. This documentation will always be up to date, which is not usually the case with traditional documentation. Code examples very efficiently describe the usage and behaviour of classes/methods etc, much more efficiently than written text. Unit tests are a very useful mixture of executable specification and usage examples.
True TDD is hard, get used to it
To get all the benefits of TDD, you always have to push yourself to follow the three rules of TDD:
1. You are not allowed to write any production code unless it is to make a failing unit test pass.
2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
This is hard. Really hard. Testing legacy code is hard. Testing the GUI is hard. Convincing colleagues is hard. Keeping your tests fast is hard.
There are no simple solutions which magically solve these hard issues. They are hard because they are hard. The only thing you can do is to do the best that you can, and do it over and over again.