What is Test Driven Development?
Test Driven Development (TDD) is the process of using coding’s failure to pass test cases to identify the coding necessary to be written by the development team. The primary feature of the process is a very short Test/Code cycle used to code a single requirement in order to pass a single test case. With TDD, we are only concerned with the tests the application FAILS. We do not write additional code if the application PASSES a test case.
The Development Scenario
Let’s explore a possible scenario and see how TDD fits into the creation of the code.
Most of you have a smartphone. And I suspect many of you use your smartphone to do such things as check your account balances, pay your cell phone bill, or even pay your water bill using one sort of an application (or app) or another. To perform these activities, the apps have a specific set of functions, or capabilities. For the sake of simplicity, let’s focus on a single app and let us look at several basic functions.
Most, if not all smartphone apps that in any way deal with money, have several functions in common. All require a User Name and a Password. The application has to be able to identify a valid match with the User Name to the appropriate Password. If there is no match, the app refuses to grant you access to the financial information. And it will, in general, inform you there has been a mismatch.
One of the latest smartphone advances is the ability of the device to read your fingerprint. How would a team using TDD implement this new capability?
Let’s look at the development of new functionality for a banking app. The required functionality (limiting ourselves to gaining access to the financial information) is as follows:
- Launch the Banking app.
- If we are using User Name and Password, capture User Name and Password. If not, skip to step 5 below (fingerprint match).
- Validate User Name and Password. If not valid, deny access to financial information and request User Name and Password or Fingerprint validation.
- If valid User Name and Password match grant access to financial information. Skip to step 7 below (access financial information).
- Or, if we are using Smart Phone fingerprint verification, capture the smartphone results.
- If a fingerprint match is verified, grant access to financial information. Display financial information.
- Access financial information.
The roughly stated process looks like:
The red flows and processes represent a new capability, the use of fingerprint information to gain access to your financial information. The black processes are capabilities already available. In this case, the red objects represent the use of the smartphone fingerprint verification output. The black objects represent the current User Name/Password access control.
The Additional Capability
We will, in this article, be using a somewhat simplified scenario, requirements, user stories, and development. As has been said earlier, the Test Driven Development approach is driven by the tests the application fails. This means we don’t develop code if the application PASSES a test case. With the banking app, the test cases would include:
- Enter User Name - The app will allow you to enter the User Name.
- Enter Password - The app will allow you to enter the Password.
- Log in - Depending on the User Name and Password values, the app will allow you to access banking data (correct combination), or it should restrict your access to the banking information (incorrect combination).
- Log out - The app will allow you to close your access to the financial information.
- The app will allow you to log in using the fingerprint verification capability.
If during the tests, the app passes a test case, according to TDD you will not develop any coding for these capabilities. The test case results determine what code is to be written. In our example, the first three test cases (Capture User Name, Capture Password, and Login functions) pass. As a result, no time will be spent developing code for these capabilities.
The testers then move to the additional, or new capabilities: using the smartphone fingerprint verification capability to log into the banking app.
Testing the Addition
To test this capability, the testers create a test case that uses the smartphone fingerprint verification output to replace the User Name and Password inputs.
The application fails the test case. The primary reason it fails (given the simplicity of our scenario) is that there is no interface between the fingerprint verification capability and the banking app. We see immediately that we have to develop coding to use the fingerprint verification output from the smartphone. The mechanics of the implementation are outside the scope of this article.
In general, the TDD process follows the chart below:
The steps are:
- Add a Test - The testers, working with the requirements, develop a set of test cases to test the new capability.
- Run the tests - The testers run tests against the current software.
- If the software passes a test case, we move to the next test case and requirement.
- If the test case fails, the developers make some changes to the coding to address the failure.
- Retest the coding.
- If the coding passes, move to the next requirement and repeat the process.
The failure determines where the developers’ efforts will be spent. Only requirements with failed test cases will be addressed with respect to coding.
The developers then create, usually in small increments, the coding needed to meet the requirement.
Once the application coding passes the test, coding to implement that capability stops and the developers move to the next capability to be implemented.
Obviously, the steps needed for accepting the verification, determining the value of the verification (yes or no) and acting on the verification will require more than a single line of coding. However much development is required, the goal is to create small updates in the coding until the final version does, in fact, pass the test case.
The primary advantage of TDD is it forces the development team to create test cases for the code. The code may not be any better than code developed with other methods, but at a minimum, the code will be tested.
The second advantage of the approach is that it forces the developers to look at, in concrete, pass-fail terms, when the coding for a particular portion of the software is completed (even if you may end up adjusting it when it integrates with the rest of the system.
Finally, TDD ensures that you have test cases for every single requirement/function you are coding to. This is a Quality Assurance dream come true.
Cedric Buest, a Google software engineer and the creator of the Java testing framework, lists two disadvantages with TDD.
- It promotes micro-design over macro-design. Remember, the idea in TDD is to create the simplest code that could possibly work. It doesn’t necessarily take into account the next milestone in software development. As a result, as you build the software, you are likely to revisit the previous coding in order to attach hooks or other characteristics that, at the time, you didn’t need. The example Buest uses is building the first floor of an apartment building without taking into account all of the characteristics you are going to need in order to support the rest of the floors.
- It’s hard to apply in practice. Quite often, in what people like to refer to as “the Real World”, TDD does not work well with systems that consist of hundreds of thousands of lines of code. As Buest points out, TDD may work well with developing a bowling score card, but the process will likely run into difficulty if the goal is to create code to interface with a 20-year-old mainframe maintaining client information for a utility. The more complex the code being modified, the more moving parts there are, and the more difficult it becomes to do a minimalistic job on the coding.