Test Driven Development (TDD to its friends) is a well-blogged topic. There are many examples of TDD around the blogosphere, crossing a multitude of technological frontiers. What follows is my own, slightly tongue-in-cheek overview of what TDD is.
Methodology
TDD is essentially a process that states the acceptable criteria of something before building it. In code, this is typically the output of a method, but there are loosely analogous real world examples. It could be argued that examinations are tests that classify candidates as acceptable or unacceptable. A candidate with a perfect score is equivalent in this case to a method which satisfies all its unit tests. And anything with driven in its name is just begging for a car analogy. So instead of talking about either of those things, I’m going to build the Death Star.
The first thing we need to do is describe the Death Star. What should it look like? What should it do? What are its essential features? We describe it with a series of questions. Anything we subsequently build which can give satisfactory answers to those questions is to all intents and purposes the Death Star.
These questions are unit tests. They test a single scenario for a particular method. Methods can have more than one unit test depending on their complexity, but where possible it is best to break complex methods into smaller, more easily testable components.
Below are two basic requirements of the Death Star, along with the unit tests which would need writing before implementation.
That’s no moon
How big should the Death Star be? The only acceptable answer here is as big as a moon. Our test should be to measure the diameter of our object, in Imperial Moons.
Fully operational
The Death Star should be fully operational. We can test this by attempting to destroy Alderaan.
Implementation
When the unit tests are first written, they will fail because we have no implementation. The next step is to build the Death Star itself, in such a way that it satisfies each test.
One immediate problem that presents itself is the requirement for it to be fully operational. The test involves destroying Alderaan, something which we can only do once. To get around this, we require a mock Alderaan, which we can create in our unit test with the intention of using it to test the Death Star’s destructive function. This removes the test’s dependency on an external resource, and the same approach is taken when testing methods dependent on, say, database operations.
Stay on target
Although TDD can give developers confidence that the code they write will satisfy requirements and can be refactored with confidence, it does not guarantee that the final code is problem-free. It can only test what has been anticipated.
However if the input and output of each method is covered by tests, we can be confident that the system as a whole will work as designed. In out Death Star example, the reactor core would also have unit tests, as would all the components it interacts with. The situation where an unexpected input (such as a proton torpedo) causes an unexpected output, which is then input into the next component until a chain reaction destroys the whole system would never arise with good test coverage.
Conclusion
I hope this brief overview of TDD demonstrates that as a methodology it isn’t that difficult a concept. The actual implementation of unit tests themselves is a much wider topic however, with a rich selection of frameworks to choose from. As primarily a C# developer, I have no serious complaints about NUnit, which is more or less the de facto standard now, although Microsoft does ship Visual Studio with its own unit testing framework, which may work well enough for yourself.
Leave a Reply