TDD for improved software health
There follows an article first published in the September 2005 Objektum newsletter, it is repeated here for the interest of my readers.
There is, in the software industry, a current drive towards the development of more robust, stable and generally better quality software. One method of achieving this goal is to employ test driven development (TDD). As with many techniques, this is nothing new; in fact similar approaches to software development were employed way back when the cost of computing time prohibited extensive debugging.
One of the best ways to produce robust software is to test it. Testing, however, has traditionally been neglected by software developers and left until the very end of the lifecycle. Patently, this is a somewhat fool hardy if familiar situation:
- When do what to find problems? As early as possible.
- When do you want to fix problems? As soon as possible.
- When do we test applications? As late as possible!
If a person were to become ill, they would see a doctor, who would examine them, establish the problem and prescribe the appropriate solution to recover health. A healthy person is responsive flexible and dynamic.
Like the unhealthy person, badly written software performs poorly and is very difficult to adapt or change while healthy software is efficient, adaptable and changeable.
There is a continuous drive to quality software. Quality software and healthy software share many goals; correct behaviour, appropriate documentation, conformance to standards. While behaviour is important, a methodology that places this above all else can result in extremely delicate software. Healthy software, by contrast, is robust under change. If software is healthy then we need not be afraid to refactor when the need arises. We need not take the long way around implementing new functionality simple to avoid changing something that apparently works.
Software has no concept of its own health, there are only symptoms. The health of software can be established by testing. Driving the software development by testing leads to healthy software and is known, unsurprisingly as Test Driven Development.
Test Driven Development is a test first approach and is in fact a simple approach:
- First consider the required behaviour.
- Write the test imagining that the functionality is already available.
- Next write just enough application code to pass the first test.
- Add tests and build up the application code until all of the required functionality is available and tested.
Over time one will build up a large suite of tests that should be run regularly to ensure that software remains healthy and functionality does not regress. Software health is encouraged by Test Driven Development (TDD) because, if a bug is found, the first task should be to write a test to highlight the bug. Once the code passes the test the problem has been resolved and since the test is now part of the test suite, this bug can not re-occur in a later increment.
This approach to bugs is particularly useful part way through a development program. Developers start to add tests where they find bugs. Soon the areas with the most bugs will become the most tested areas and hence the healthiest part of the code. The whole process is, in some way, self-healing and leads to an equilibrium of quality across the product.
Practically, a test suite has been built up that proves our software is correct and the achievement of software health has arrived somewhat by accident.
Since a thorough suite of tests exist. The code can be refactored with confidence using the existing suite of tests as a safety net while adding new tests to build up the new functionality. If a change requires a significant refactoring then we may need to also update the tests. This update will be done incrementally,test first.
Without these tests, refactoring has traditionally been a somewhat risky undertaking, often breaking what used to work.
A test is a piece of code that can demonstrate a feature or element of functionality of the software. Each testmust be self-contained and is just enough to test. In a TDD approach the code does just enough to pass thetest. Tests raise assertions if elements of them fail, thereby identifying areas of concern.
In order to fully leverage the benefits of TDD an environment that supports rapid development and running of tests is needed. In this environment, it must be possible to build large hierarchies of tests and also execute subsets of the hierarchy. In addition it should be possible to isolate ‘fixtures’; that is the setup code required before the test and cleanup for afterwards, from the test methods themselves.
Kent Beck, a principle figure in the field of Test Driven Development, developed such a framework called the xUnit framework. While the framework was initially implemented for Smalltalk it has since been ported to other languages in particular to Java with JUNIT but also to C++, C#, PHP and many more.
Having used xunit ports on both Java and PHP the standard of the C++ ports is somewhat disappointing. The xunit implementations I tried were built using a combination of scripting languages and macros. Macros are not type safe and are really a hand over from C. If we cannot develop a test framework in C++ are we really qualified to be developing application software?
With this in mind I have developed a pure C++ implementation of xunit which uses templates as opposed to macros thereby making the framework type safe. This framework is aptly named xunit-cpp and is available from the resources area at www.objektum.com and is freely distributed under the GPL.
The software is fully auto documented using doxygen. Currently this is a minimal implementation providing a proof of concept that a type safe robust xunit framework can be implemented for C++. I am hoping to add integration with the Eclipse IDE.
This framework can be used to have a go at developing code using test driven development.
Below is an example test case that shows how a simple test case could be written.
The down side of a pure C++ approach is apparent in two areas. First one must explicitly name the TestCase and also the test method. Secondly we must add test methods to the test case as shown in the constructor below. It is not sufficient to simply add a new method to the class as in JUnit. This is because the C++ language is lacking the reflection capabilities and class based operations of Java.
class MyFirstTest : public TestCase
{
public:
MyFirstTest():TestCase("My first test")
{
addTestMethod(makeTestMethod(this,&MyFirstTest::doTest,"test name"));
}
virtual bool setUp(){return true;}; virtual void tearDown(){}; void doTest()
{
// Use assertions to test your application.
assertTrue(true, “Meaningless test”);
}
};
To run a test you must simply write a main function and call run on you TestCase implementation. Alternatively build up a hierarchy using instances of TestGroup and call run at any point in your hierarchy.
int main ()
{
MyFirstTest test;
test.run();
test.print();
}
The output produced by print will show the results of test methods within the hierarchy of test cases andtest groups. A failed assertion will be highlighted including the given name to aid debugging.
Since the xunit-cpp framework is pure C++ it is completely platform independent. The template based approach used ensures type safe behaviour that is over and above the text based approach used in macros. In addition any debugger can be used to step through your tests and isolate the cause of a failed assertion.
Download it, give it a try and start using Test Driven Development to improve the health of your software.

