CCUnit  2.1
A C Unit Testing Library
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
CCUnit Cookbook

Here is a short cookbook to help you get started.

[see also Japanese documents]

Simple Test Case

Tests in CCUnit can be run automatically. They are easy to set up and once you have written them, they are always there to help you keep confidence in the quality of your code.

To make a simple test, here is what you do:

  1. Create a test function
  2. When you want to check a value, call CCUNIT_ASSERT(bool) and pass a bool that is true if the test succeeds

    ASSERT macro is listed in others as well in Assert Macros.

For example, see the test of the library of the simple complex number as an example. (This sample program is in the examples/complex directory) To test whether the initialization of the complex numeber and two complex numbers are equal.

void test_complex_new ()
{
complex_t* c10_1 = complex_new (10, 1);
CCUNIT_ASSERT_EQ_DOUBLE (10, c10_1->real);
CCUNIT_ASSERT_EQ_DOUBLE (1, c10_1->imaginary);
complex_delete (c10_1);
}
void test_complex_equals ()
{
complex_t* c10_1 = complex_new (10, 1);
complex_t* c10_1_2 = complex_new (10, 1);
complex_t* c1_1 = complex_new (1, 1);
CCUNIT_ASSERT (complex_equals (c10_1, c10_1_2));
CCUNIT_ASSERT (complex_equals (c10_1, c1_1) == 0);
complex_delete (c10_1);
complex_delete (c10_1_2);
complex_delete (c1_1);
}

These two test functions are made one test case together, and we can run it.

Include the necessary header files.

First, Allocate the test case memory.

extern void test_complex_new ();
extern void test_complex_equals ();
int main ()
{
CCUnitTestCase* testCase;
bool success;
testCase = ccunit_newTestCase ("complex test");

Then, register the test functions in the test case. The assertion should specify the name of the function and the explanation of the function to indicate it at the unsuccessful time.

"test_complex_new",
"complex new test",
test_complex_new);
"test_complex_equals",
"complex equals test",
test_complex_equals);

Run the test case, and take out the result.

result = ccunit_runTestCase (testCase);
success = ccunit_wasSuccessful (result);

Deallocate the memory whitch you assigned, and return the result.

return success ? 0 : -1;
}

You find the sample code made in the above in the files testComplex.c and runTestCase.c in the examples/complex directory.

To compile and run, do the following:

$ gcc -o runTestCase runTestCase.c testComplex.c complex.c -lccunit
$ ./runTestCase
$ echo $?
0

When the test case is run, that specific test functions will be run. This is not a useful thing to do, however, as no diagnostics will be displayed. One will normally use a Executing Test to display the results.

TestRunner

How do you run your tests and collect their results? The TestCase store result of own tests into the TestResult. At the same time, the test case informs the TestRunner of the executive conditionsn of the test.

dot_inline_dotgraph_1.png

Once you have a test suite, you'll want to run it. CCUnit provides TestRunner to define the suite to be run and to display its results.

To use the TestRunner, include the header files for the tests in runTestCaseRunner.c:

First, make the TestSuite, and register the TestCase.

extern void test_complex_new ();
extern void test_complex_equals ();
int main ()
{
CCUnitTestCase* testCase;
int ret;
testCase = ccunit_newTestCase ("complex test");
"test_complex_new",
"complex new test",
test_complex_new);
"test_complex_equals",
"complex equals test",
test_complex_equals);
suite = ccunit_newTestSuite ("complex test suite");
ccunit_addTestCase (suite, testCase);

And call to ccunit_runTestRunner(CCUnitTestRunner*, CCUnitTestSuite *):

runner = ccunit_newTestRunner (stdout);
ret = ccunit_runTestRunner (runner, suite);

Deallocate the memories.

/* ccunit_deleteTestCase (testCase); // testCase was deleted when suite was deleted */
return ret;
}

The sample code made in the above is in the directory examples/complex, the files testComplex.c, complexTestSuite.c and runTestRunner.c.

To compile and run, do the following:

$ gcc -o runTestCaseRunner runTestCaseRunner.c testComplex.c complex.c -lccunit
$ ./runTestCaseRunner
..
Time: 0.000027 sec
OK (2 tests)

The TestRunner will run the tests. If all the tests pass, you'll get an informative message. If any fail, you'll get the following information:

For example, try to mistake it in the test case of testComplex.c on purpose.

void test_complex_new ()
{
...
CCUNIT_ASSERT_EQ_DOUBLE (10, c10_1->real);
/*CCUNIT_ASSERT_EQ_DOUBLE (1, c10_1->imaginary);*/
CCUNIT_ASSERT_EQ_DOUBLE (1.1, c10_1->imaginary);
...
}
$ gcc -o runTestCaseRunner runTestCaseRunner.c testComplex.c complex.c -lccunit
$ ./runTestRunner
.F.
Time: 0.000051 sec
FAILURES!!!
Test Results:
Run 2, Failures 1
There was 1 failure:
testFailComplex.c:36: complex new test:
1.1 == c10_1->imaginary
expect: 1.100000
actual: 1.000000

In this example, two test cases are run. Then, you will see that the second is unsuccessful.

That was a very simple test. Ordinarily, you'll have many little test cases that you'll want to run on the same set of objects. To do this, use global variables.

setUp and tearDown

What if you have two or more tests that operate on the same similar set of objects?

dot_inline_dotgraph_2.png

Tests need to run against the background of a known set of data. you are writing tests you will often find that you spend more time writing the code to set up the data than you do in actually testing values.

In the sample code of the previous section, some complex numbers are allocated, and it is deleted.

Often, you will be able to use the same data for several different tests. Each case will assign slightly different values to the data and will check for different results. Then, if the test is finished, clean-up the data. The function setUp and tearDown are used at such time.

dot_inline_dotgraph_3.png

When you have a common global variables, here is what you do:

Define the global variable.

#include "complex.h"
static complex_t* c10_1;

Write the function setUp to allocate the complex.

void setUp_test_complex ()
{
c10_1 = complex_new (10, 1);
}

Write the function tearDown to delete the memory of that complex.

void tearDown_test_complex ()
{
complex_delete (c10_1);
}

Write the test function.

void test_complex_new ()
{
CCUNIT_ASSERT_EQ_DOUBLE (10, c10_1->real);
CCUNIT_ASSERT_EQ_DOUBLE (1, c10_1->imaginary);
}
void test_complex_equals ()
{
complex_t* c10_1_2 = complex_new (10, 1);
complex_t* c1_1 = complex_new (1, 1);
CCUNIT_ASSERT_TEST_OBJ (c10_1, complex_equals, c10_1_2, complex_to_string);
CCUNIT_ASSERT_TEST_OBJ (c10_1, !complex_equals, c1_1, complex_to_string);
complex_delete (c10_1_2);
complex_delete (c1_1);
}

Register the function setUp and tearDown. At this time, the name of the function must have begun with "setUp" or "tearDown".

int main ()
{
CCUnitTestCase* testCase;
int ret;
testCase = ccunit_newTestCase ("complex test");
"setUp_test_complex",
"setUp_test_complex",
setUp_test_complex);
"tearDown_test_complex",
"tearDown_test_complex",
tearDown_test_complex);

Run the test case, and return the result.

"test_complex_new",
"test new",
test_complex_new);
"test_complex_equals",
"complex equals test",
test_complex_equals);
suite = ccunit_newTestSuite ("complex test suite");
ccunit_addTestCase (suite, testCase);
runner = ccunit_newTestRunner (stdout);
ret = ccunit_runTestRunner (runner, suite);
return ret;
}

The sample code made in the above is in the directory examples/complex, the files are testComplexSetup.c and runTestCaseSetup.c.

To compile and run, do the following:

$ gcc -o runTestCaseSetup runTestCaseSetup.c testComplexSetup.c complex.c -lccunit
$ ./runTestCaseSetup
..
Time: 0.000048 sec
OK (2 tests)

setUp/tearDown of only one time

In the previous section, the initialization was done in every test every time. But we do the initialization only once, and we may run the multiple test. Then, only when all the tests are finished, it may seem to carry out the code for the settlement.

You register the setUpBeforeClass and tearDownAfterClass function in the test case for that. Then, the test functions are run in the following order:

dot_inline_dotgraph_4.png

Suite

How do you set up your tests so that you can run them all at once? Once you have several tests, organize them into a suite.

dot_inline_dotgraph_5.png

CCUnit provides a TestSuite module that runs any number of TestFuncs together. You saw, above, how to run test suite.

To create a suite of two or more tests, you do the following:

Create two test cases, and register test functions to each cases.

int main ()
{
CCUnitTestCase* testCase, * testArith;
int ret;
/* test complex */
testCase = ccunit_newTestCase ("test complex");
"setUp_test_complex",
"setUp_test_complex",
setUp_test_complex);
"tearDown_test_complex",
"tearDown_test_complex",
tearDown_test_complex);
"test_complex_new",
"test new",
test_complex_new);
"test_complex_equals",
"complex equals test",
test_complex_equals);
/* test complex arith */
testArith = ccunit_newTestCase ("test complex arith");
"setUp_test_complex_arith",
"setUp_test_complex_arith",
setUp_test_complex_arith);
ccunit_addNewTestFunc (testArith,
"tearDown_test_complex_arith",
"tearDown_test_complex_arith",
tearDown_test_complex_arith);
"test_complex_add",
"test add",
test_complex_add);
ccunit_addNewTestFunc (testArith,
"test_complex_sub",
"test sub",
test_complex_sub);
"test_complex_mul",
"test mul",
test_complex_mul);
ccunit_addNewTestFunc (testArith,
"test_complex_div",
"test div",
test_complex_div);

Create a test suite, then register that two test cases to it.

suite = ccunit_newTestSuite ("complex test suite");
ccunit_addTestCase (suite, testCase);
ccunit_addTestCase (suite, testArith);

Create test runner and run the tests.

runner = ccunit_newTestRunner (stdout);
ret = ccunit_runTestRunner (runner, suite);
return ret;
}

The sample code made in the above is in the directory examples/complex, the files are runTestSuite.c, testComplexSetup.c and testComplexArith.c.

To compile and run, do the following:

$ gcc -o runTestSuite runTestSuite.c testComplexSetup.c testComplexArith.c complex.c -lccunit
$ ./runTestSuite
......
Time: 0.000061 sec
OK (6 tests)

TestSuites don't only have to contain TestCases . They can also contain TestSuites. For example, you can create a TestSuite in your code and I can create one in mine, and we can run them together by creating a TestSuite that contains both:

CCUnitTestSuite* suite_a;
CCUnitTestSuite* suite_b;
...
suite = ccunit_newTestSuite ("complex all test suite");
suite_a = ccunit_newTestSuite ("complex test suite A");
suite_b = ccunit_newTestSuite ("complex test suite B");
...
ccunit_addTestSuite (suite, suite_a);
ccunit_addTestSuite (suite, suite_b);

Helper Tool

As you might have noticed, the work to register the test function in the test case and to register the test case in the test suite is easy to mistake concerning the repetition.

Actually, though the test function of test_complex_to_string() was defined in testComplexSetup.c, it was ignored by the above explanation, and it didn't register for the test case.

A Creating TestSuite set of functions and command have been created to automatically implements the creating suite function.

You make the file which you write the test case in for that as follows.

First, declare the test suite as this in the comment of the javaDoc style. As for the comment of the javaDoc style, the starts of the comment block of the C style are two asterisks "<tt>**</tt>".

/** test suite: complex test suite */

Next, define the test cases. The test case is gathering of the global variables and the functions that surrounded in the declaration of the end of the test case with the declaration of the name of the test case written in the comment of the javaDoc style.

One file can define the multiple test case.

/** test case: complex test case */
static complex_t* c10_1;
/** complex new test */
void test_complex_new ()
{
...
}
...
/** end test case */
/** test case: complex arith test case */
...
/** end test case */
/** end test suite */
/** test suite: ... */
...
/** end test suite */

As for the test function, the name must have begun with test. You can write the explanation of the test function in the comment of the javaDoc style before each test function. If the comment isn't being written, the name of the test function is registered as an explanation of the function.

There are setUp, tearDown, setUpBeforeClass and tearDownAfterClass as a special test function. Their works as a functional function of the test case that Those function as a functional function of the test case which I explained before so that you may know it from that name. Define as the function to begin respectively with such a name.

To generate creating suite function code, run ccunit_makeSuite tool.

The function to make TestSuite is ccunit_suite. But, it can be changed to another name by the -f option of the ccunit_makeSuite command, too.

$ ccunit_makeSuite -f suite_test_complex -o suiteTestComplex.c testComplexSetup.c testComplexArith.c

The next is taken to use the code which you generated automatically.

Declare the prototype of the function which you generated. Try not to forget char* name of the argument.

#include <stdio.h>
extern CCUnitTestSuite* suite_test_complex (char* name);

Call the function which you generated, and take out the test suite.

int main ()
{
int ret;
suite = suite_test_complex ("complex suite auto generated");

Run it by the test runner.

runner = ccunit_newTestRunner (stdout);
ret = ccunit_runTestRunner (runner, suite);
return ret;
}

The sample code here will be found with runTestSuite.c, testComplex.c and testComplexArith.c of the examples/complex dhirectory.

To compile and run, do the following:

$ ccunit_makeSuite -f suite_test_complex -o suiteTestComplex.c testComplexSetup.c testComplexArith.c
$ gcc -o runTestSuiteAuto runTestSuiteAuto.c testComplexSetup.c testComplexArith.c suiteTestComplex.c complex.c -lccunit
$ ./runTestSuiteAuto
.......
Time: 0.000060 sec
OK (7 tests)

To generate a code, the test case source codes must be formatted by following pseudo-BNF:

SOURCEFILE ::= SUITE... | TESTCASE...
SUITE ::= SUITE_HEADER
[ any-C-code ]...
TESTCASE...
SUITE_END
SUITE_HEADER ::= JAVADOC_COMMENT_BEGIN 'test suite:' SUITE_NAME COMMENT_END
SUITE_END ::= JAVADOC_COMMENT_BEGIN 'end test suite' wsp string COMMENT_END
TESTCASE ::= TESTCASE_HEADER
[TESTCASE_CODE]...
[TESTFUNC]...
TESTCASE_END
TESTCASE_HEADER ::= JAVADOC_COMMENT_BEGIN
'test case:' TESTCASE_NAME
COMMENT_END
TESTCASE_CODE ::= any C language codes.
TESTFUNC ::= [ FUNC_DESC ] 'void ' FUNC_PREFIX[A-Za-z0-9_]* '()' FUNC_BODY
FUNC_PREFIX ::= 'test' | 'setUp' | 'tearDown' |
'setUpBeforeClass' | 'tearDownAfterClass'
TESTCASE_END ::= JAVADOC_COMMENT_BEGIN 'end test case ' [string] COMMENT_END
FUNC_DESC ::= JAVADOC_COMMENT_BEGIN string COMMENT_END
FUNC_BODY ::= '{' C language codes... '}'
JAVADOC_COMMENT_BEGIN ::= '/' '*' '*'
COMMENT_END ::= '*' '/'

Sample Programs

The sample program made in the above is in the examples/complex directory.

SourceForge.jp hosts this site. Send comments to: CCUnit Developer