Rajinder Yadav - Windows/Linux C++ Development Tools & Resources :Design, Code, Test, Deploy

What is Unit Testing?

If you have not been exposed to an automated unit test (UT) framework before then you might not quite be sure what unit testing is. Although it’s most likely you have written some form of tests for the code you have developed.

What most developers do is write some kind of “test” code that makes calls to the code they are working on for a project such as an API or a C++ class or even a module. If it’s a C++ class that need to be tested, the developer will instantiate a class object and make use of it by calling various methods. Most likely this will be done by stepping though the code using a debugger. This basically constitutes a set of UTs, and is also a form of white-box testing.

White-box testing is having access to the code being tested and thus being able to apply an input and see how the expected output or state change occurs. White-box testing is what you do when you step though code using a debugger.

During the initial development phase, the developer will perform white-box testing with various inputs and he will exercise each execution path to make sure functional requirements are satisfied and that errors are handled correctly. The drawback with this type of unit testing is that it’s very tedious, slow and possibly prone to error. This can happen when the developer makes a change to the code and forgets to execute one of his unit test cases.

So this is where the UT framework comes to the aid of the developer. UT allows the developer to write test code once and have the UT framework automate the process of running each test case.

A UT framework adds structure and consistency to the testing process

UT is what one might call grey-box testing or black-box testing. With grey-box testing you still have access to the source code, but unlike stepping though with a debugger you can’t alter variables on the fly to test each execution path! But what you may be able to do is try to finesse each test case to test each execution path, but this is always not possible when there are other dependencies.

With black-box testing, you have no access to the code, the only thing you know is the interface or method signature. So all you can UT is the expected output for each input and make sure the code functions to specification! Most of the UT cases you write will be black-box testing, with a few UT cases that are grey-box testing.

So basically a UT is testing one specific API or class method with one specific input.

Some might argue that testing a module is not unit testing in the pure sense of the terminology. So the way I like to think of UT is in terms of functional testing, and functional testing could involve many APIs or class objects, such as when testing a database for table insertion.

What a UT Should Cover

With UT, you always start at the smallest level first and make sure code is working as designed. Once the low level has been thoroughly covered you can move on to higher level functional testing.

When constructing a UT, test for:

Test what should work, after all if this is broken then you have not satisfied functional requirements.

Test for robustness, try to break the code, get it to crash or fail and make sure the code handles error conditions gracefully either by returning an error code or not doing anything. For example, if you test trying to open a file that does not exist, the call should not crash somewhere later in the code. Think of an application that logs events to a file. If for some reason the log file is deleted by another process or the Users, the program should be able to continue without hanging or crashing even if returning an error is not important to the application.

Do not be afraid to build mock objects that may be required to be set before a test case can be run.

Try to develop code with loose coupling so that less work is required to UT a piece of code in isolation.

Conclusion

The beauty of having a UT framework is that it allows you some comfort in knowing you can go in and change the way a method is implemented, refactor code, fix a bug and then have a mechanism to test the changes. Being able to automate regression testing to assure nothing was broken in the process adds to the assurance of code quality, reliability and robustness.

Automated regressing testing should always be performed before code is committed to source control for a bug fixed, if this is not feasible because of the amount of time to run all the test cases, then UT should always be performed at the end of each daily-build to make sure no new bugs or code error were introduced with the latest changes. Over time as bugs are discovered and fixed you will have new UT cases to add. UT is only one piece of the testing process, it cannot catch every possible error that can be causes when developing software such as integration problems, or performance issues or some other system and resource issues.

I am not a proponent on write UT cases first then code later, I would suggest you do both in tandem. Once one piece of testable code is complete you should focus on building a set of UT cases for it.

Home

Copyright © 2007 Rajinder Yadav, All rights reserved