Unit testing provides your first line of defense against application flaws

Of course each of your programs is a work of art. Yet despite all that elegant code and superb functionality, bugs still find their way in. Unit testing can help. Learn the best way to define test cases and implement this part of the testing life cycle.

You’ve just spent hours developing your program, and it’s a thing of beauty. In fact, it’s so elegant and well thought-out, testing is just a formality. Well, you think, I might as well go through the motions. What’s this? The program blew up! Ah, a simple error. How could I have overlooked that? Let’s try it again. Darn, another error. Oh yeah, I forgot to define what should happen if that condition was true. Okay, the third time’s the charm. Man, another error. This is going to be more difficult that I thought….

Does this sound familiar? It does to me. I could write thousands of lines of code in a couple days, and I always had the notion that it would work fairly smoothly the first time. It rarely did. Here are two techniques for checking your code to make sure that your users see your code the way was meant to go out—error free.

Who does the testing?
When your overall testing plan is in place (see "Think ahead: Defining your testing strategy" and "Hammer out your tactical testing decisions with a Testing Plan" for more information on earlier stages of the testing process), your first defense initiative in the testing wars is the unit test. The unit test is the one we are all most familiar with. In my experience, the original developer does the unit test. However, because the original developer can unintentionally bias the testing around what he or she expects from the code, some organizations have an independent testing team perform the unit testing.

Start thinking about unit testing early
The biggest problem developers have with unit testing is that they consider it an afterthought once the coding is complete. This is probably okay with smaller projects and smaller pieces of code, but for large projects, it’s a mistake. Remember that testing has a life cycle of its own and that planning ahead is one of the most important aspects of this cycle. For unit testing, this planning involves defining a set of test cases needed to thoroughly test the program. Most test cases should be defined ahead of time during the Design Phase of the project, since that’s where you have a good understanding of the programs, screens, and reports required to complete the solution. Each of these entities is established to fulfill certain business requirements, so that’s when you should be able to describe the test cases needed to unit-test each component. Remember, you don’t need to create the test data at that point—only the test cases.

Waterfall and RAD approaches
Note that creating the unit test cases during the Design Phase implies a waterfall-like development life cycle. If you’re using an iterative approach, like Rapid Application Development (RAD), you will end up with a series of analysis/design/construct steps. In this approach, document as many test cases as you can, based on the business requirements generated in that iteration, and add them to test cases created in prior iterations.

Testing serves two main purposes. First, it lets you validate that the solution meets the business requirements. Second, it allows you to find errors. You might think that unit testing involves thinking of as many test cases as possible. Actually, for a complex program, you could generate an infinite number of test cases. You don’t have that much time for your testing. Instead, use two testing techniques to rigorously test your code with a reasonable number of test cases: black box and white box testing.

Black box testing
Black box testing looks at the program’s functionality against the business requirements. As the name implies, this type of testing requires you to approach your program as though it were a black box. You are not concerned with its inner workings. All you know is that when you give it a certain set of inputs, it gives back a certain set of outputs. This is true whether the program is a report, a Web page, or an internal calculating procedure. All programs work on some set of inputs and create some set of outputs.

An example of black box testing is a Web page that takes a numeric employee ID and returns a corresponding name, address, and telephone number. You can generate three test cases to test this requirement. First, test with a valid numeric employee ID. Second, test with an invalid numeric ID. Finally, enter an employee ID with alphas. Theoretically, you could enter test cases for all valid employee IDs, and you could test with an infinite number of invalid IDs. But you don’t need all of them. Three test cases for this particular example should result in proper test coverage.

Another black box testing technique is to test at the boundaries if the requirement includes a valid range of input. For instance, if a certain field can hold a number from 1 to 100, you want to test at the boundaries of the range. So, you could create five test cases of 1, 100, 0, 101, and an alpha. Again, although there is an infinite number of possible test cases, those five should suffice.

White box testing
White box testing uses the opposite approach. Here, you look at the structure of the internal code and try to devise test cases that will exercise all of the code, or as much of it as possible. For instance, whenever you have a conditional logic statement (IF-THEN-ELSE), you want to generate a test case that will ensure that each branch of the code is executed. Of course, then you need to validate that the resulting output is as you expected.

White box testing is complementary to black box testing. You shouldn’t limit your unit testing to white box only. Just because each part of the code is exercised does not mean that the results are accurate. You must know the expected result from a black box perspective to make sure that the result is accurate.

One particular value of white box testing is that it exercises sections of code that might not get tested otherwise. For instance, a certain combination of inputs may be necessary for certain sections of code to execute. Using black box techniques may not result in the right set of inputs. However, by looking at the code, you can see what set of events is required to exercise a set of instructions, and then you can build a test case around that scenario.

Obviously, you must be able to examine the code to develop the test cases for white box testing. Therefore, unlike black box testing, which allows you to develop test cases during the Design Phase, you need to wait until the Construct Phase to create the white box test cases.

Things to remember
Here’s a rundown of the main points to help you cover all the bases during unit testing:
  • Unit testing is the first line of defense in the testing process. You want to catch as many errors as possible at this point, since errors that are caught in later testing results in more and more rework.
  • Black box techniques focus on ensuring that the proper outputs are received based on a set of inputs. These tests make sure that the program functions according to the business requirements. You should design black box test cases during the Design Phase.
  • White box testing involves creating test cases to try to exercise as much of the program code as possible. This helps test code logic that may not be executed with black box testing. Also, you can’t build these test cases until the Construct Phase.
  • Black box testing is most important, but a combination of the two will result in a rigorous testing of the code with the least amount of test cases.

Next steps
Even though the test process always takes longer than you would like, eventually all programs get through it. However, it is a rare solution that has only one independent module. There are usually many programs, databases, and components that need to function together to make up the complete solution. So our next column will focus on integration testing (some people call this string testing), where we’ll combine the unit-tested pieces to make sure they perform as expected.

Project management veteran Tom Mochal is director of internal development at a software company in Atlanta. Most recently, he worked for the Coca-Cola Company, where he was responsible for deploying, training, and coaching the IS division on project management and life-cycle skills. He's also worked for Eastman Kodak and Cap Gemini America and has developed a project management methodology called TenStep.

Editor's Picks