assert() and Unit Testing

When covering testing, we indicated that we wanted to 1) Build up a suite of tests (i.e., a test suite), 2) Run all your tests with one command, and 3) Get regression testing for free. We also want tests to be as easy to create as possible once we know the test case.

Many frameworks and tools are available to assist in creating, managing, and running test cases. The type of tests we will write are unit tests; for these, there are unit-testing frameworks. One of the first and most popular was JUnit for Java programs. JUnit is based on SUnit for the Smalltalk by Kent Beck. The framework has been translated into unit-testing frameworks for multiple languages, collectively known as xUnit.

Learning a complete unit-testing framework is very beneficial but is also quite involved. So, we will construct unit tests using features already available in most languages.

First, let's look at a single test. In this case, we will test a simple add function, add(int n1, int n2); declared in the include file add.hpp:

First, we set up any variables we need. Then, we call the function we are testing and store the result. To check the result, we use the assert() macro. An assertion in programming is something that should always be true. The assert() takes a condition. If, during runtime, the condition is true, i.e., we "passed the assertion", then the program goes on. If the condition is false, i.e., we "failed the assertion". The test case above passes successfully. So, let's look at a test case that fails.

This test should pass if the add() function is implemented correctly. But this is what happens when we run it:

When an assert() fails, it prints out an error message with the condition, function name, file number, and line number where the assertion failed.`

So, when an assertion fails, we do not get the expected result. Either our test case is incorrect (could happen, but not as likely), or the function add() is not working correctly (more likely). A glance at the implementation of the add() shows us the problem:

A couple of notes about using assert() in this way:

The other issue is managing the test cases. Here, we have one test case per file, and each test case is a separate program. There are situations, especially using external test management tools (e.g., ctest), where this is okay. However, for our purposes, it is a pain to build and run all the test cases. Another way is to combine the test cases into one test programand build and run that one test program. Here is our combined test program:

In this form, each test case is a separate block statement in the main program. By using a separate block for each test case, we can repeat variable names and create the start of a new test case by copying and pasting. We only build and run one test program; file management is much easier. Even if we decide to have multiple test files, we can still put multiple test cases in each test file.

This approach has one downfall. As soon as an assert() fails, the program stops, so we don't run the test cases that follow it. To get around this: