Introduction
This whitepaper explains how Test Automation tools can be used with C++ to support Test Driven Development (TDD) in an Agile-programming environment. This paper assumes some basic familiarity with Test Automation products. Traditional Software Development Most projects currently use a test automation tool in a traditional development environment where individual C++ classes are unit tested as they are developed. This pure unit testing allows the developer to ensure that the low-level requirements attributed to a unit are implemented correctly, while at the same time verifying the completeness of the testing via code coverage analysis.
A common solution to this is to introduce code inspections or code static analysis. However, code inspection is expensive and can become complicated when verifying complex algorithms or system behaviors. On the other hand, static analysis only verifies there are no ambiguities in the code written, rather than the code that is actually correct. Additionally, there are some key points to consider.

Figure 1 - Traditional Software Development - Waterfall Methodology
Since the code has already been written at this point, there are a number of issues in working this way:
- The identification of a potential defect in the code developed is delayed by several days, if not weeks, after it has been written
- There is a risk that developers will create unit tests based on the code developed, rather than deriving them directly from the requirements
- Code is over engineered, and more code is developed than is required to satisfy the project requirements

Figure 2 - Cost of Fixing Defects in the Software Development Lifecycle
- The scope of most errors is fairly limited. “Eighty percent of the errors are found in twenty percent of a project's classes or routines.” (Endres 1975, Gremillion 1984, Boehm 1987b, Shull et al 2002).
- Most common errors are the programmer’s fault. “Ninety-five percent are caused by programmers, two percent by systems software (i.e.; compiler and OS), two percent by some other software, and one percent by the hardware.” (McConnell, Steve. Code Complete. Redmond: Microsoft Press, 2004)
- The developer writes a test case to verify a desired improvement or new function. This test case will fail until the functionality is correctly implemented
- The developer then produces code to pass that test
- Once the code is passing the developer refactors the new code to acceptable standards

Figure 3 - Test Driven Development workflow
These concepts originally derive from test-first programming which is a core part of extreme programming that originated in 1992. However, in more recent years Test Driven Development has become a more general topic in its own right. This could be further simplified into what is called the 3 Laws of Test Driven Development. - You may not write production code until you have written a failing unit test
- You may not write more of a unit test than is sufficient to fail and not compiling is failing
- You may not write more production code than is sufficient to pass the currently failing test

Figure 4 - Traceability between a test case and the underlying source code tested
Traceability between test cases and requirements As test cases must be built before the underlying code is developed, the ability to link the Test cases to the requirements used to derive them can ensure that there is a direct traceability between the test cases, the requirements, and the code that is verified. This ensures that we stay within the 3 Laws of TDD, and will make the process of refactoring our code much easier (see Figure 5).

Figure 5 - Linking Requirements to Test Cases
A Test Driven Development Example Assume that we are building a new application, and one of the modules will provide message processing. We are not sure of the complete functionality needed, but we are sure that we need to be able to send and receive messages, and further, that the messages will consist of a variable number of 32 bit integer values. We will first create a simple C++ header file and define a procedure to send messages, and a procedure to receive messages. It might look like the following:
// Message.h bool Send_Message(MessageId mId, ByteSteam &mData, MessageSize mSize); bool Receive_Message( MessageId &mId, ByteSteam &mData, MessageSize &mSize);Now rather than go forward with the implementation details of these procedures as the next step, Test Driven Development requires us to generate test cases for these two procedures. The great thing about using this approach is it gets us to think about the design details and the edge conditions before we start to write the code. For example, here are some of the test cases that should be created for this module:
- When you call Receive_Message, do you get the message back in the order that they were sent or is there a priority to the messages based on ID?
- What happens when we receive and there are no pending messages?
- What is the largest size of a message that can be sent? Is it in the most appropriate data type for the MessageSize?
- What is the range of MessageId values?
- What happens if an invalid MessageId is received?
Comments (0)





