[ CCUnit project page ] [ CCUnit home page ]

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 Making assertions .

For example, to test that the sum of two complex number which is the sum of the values of two complex numbers, write:

This sample program is in the examples/complex directory.

void test_complex_add ()
{
  complex_t c10_1 = { 10.0, 1.0 };
  complex_t c1_1 = { 1.0, 1.0 };
  complex_t result;
  complex_t c11_2 = { 11.0, 2.0 };
  CCUNIT_ASSERT (complex_equals (&c11_2, complex_add (&result, &c10_1, &c1_1)));
}

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 a fixture.

Fixture

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

inline_dotgraph_1.dot

Tests need to run against the background of a known set of objects. This set of objects is called a test fixture. When you are writing tests you will often find that you spend more time writing the code to set up the fixture than you do in actually testing values.

Often, you will be able to use the same fixture for several different tests. Each case will send slightly different messages or parameters to the fixture and will check for different results.

inline_dotgraph_2.dot

When you have a common fixture, here is what you do:

  1. Create a TestCase object

  2. Add an global variable for each part of the fixture

  3. Write setup_setUp(), setUp() function to initialize the valiables

  4. Write tearDown(), setup_tearDown() to release any permanent resources you allocated in setUp
  5. Create a TestFixture object

  6. Add TestCase objects to fixture object

For example, to write several test cases, first create a fixture:

/** TEST CASE: complex number test */

#include "complex.h"

static complex_t* s10_1;
static complex_t* s1_1;
static complex_t* s11_2;
static complex_t* sc;

void setup_setUp_complex_test ()
{
  sc = complex_new (1, 1);
}

void setup_tearDown_complex_test ()
{
  complex_delete (sc);
}

void setUp_complex_test ()
{
  s10_1 = complex_new (10, 1);
  s1_1 = complex_new (1, 1);
  s11_2 = complex_new (11, 2);
  complex_add (&sc, &sc, &c1_1);
}

void tearDown_complex_test ()
{
  complex_delete (s10_1);
  complex_delete (s1_1);
  complex_delete (s11_2);
}

int main ()
{
  CCUnitTestFixture* fixture;
  fixture = ccunit_newTestFixture ("complex test",
                                   CCUNIT_NEWTESTFUNC(setUp_complex_test),
                                   CCUNIT_NEWTESTFUNC(tearDown_complex_test));
  ccunit_setTestFixturSetup (fixture,
                             CCUNIT_NEWTESTFUNCTION(setup_setUp_complex_test),
                             CCUNIT_NEWTESTFUNCTION(setup_tearDown_complex_test));

Once you have the Fixture in place, you can write as complex Test Cases as you'd like.

Test Case

How do you write and invoke an individual test case when you have a Fixture?

For example, to test the equality of two complex number, write:

void test_complex_equals ()
{
  CCUNIT_ASSERT_TEST_OBJ (s10_1, complex_equals, s10_1, complex_to_string);
  CCUNIT_ASSERT_TEST_OBJ (s10_1, !complex_equals, s1_1, complex_to_string);
}

...

int main ()
{
  ...

  ccunit_addNewTestCase (fixture,
                         "test_complex_equals",
                         "complex equals test",
                         test_complex_equals);

One may create and run objects for each test case like this:

  CCUnitTestResult* result;
  result = ccunit_runTestFixture (fixture);
  return 0;
}

Calling sequence of test cases in a fixture is following:

inline_dotgraph_3.dot

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

$ gcc -I. -o runTestFixture runTestFixture.c testComplex.c complex.c -lccunit
$ ./runTestFixture

When the test fixture 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 TestRunner (see below) to display the results.

Once you have several tests, organize them into a suite.

Suite

inline_dotgraph_4.dot

How do you set up your tests so that you can run them all at once?

CCUnit provides a TestSuite module that runs any number of TestCases together.

You saw, above, how to run test fixture.

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

int main ()
{
  CCUnitTestSuite* suite;
  CCUnitTestFixture* fixture;
  CCUnitTestResult* result;
  suite = ccunit_newTestSuite ("Complex test suite");
  fixture = ccunit_newTestFixture ("Complex Tests",
                                   CCUNIT_NEWTESTFUNC(setUp_complex_test),
                                   CCUNIT_NEWTESTFUNC(tearDown_complex_test));
  ccunit_addNewTestCase (fixture, "test_complex_equals", "complex equals test",
                         test_complex_equals);
  ccunit_addNewTestCase (fixture, "test_complex_add", "complex add test",
                         test_complex_add);
  ccunit_addNewTestCase (fixture, "test_complex_sub", "complex sub test",
                         test_complex_sub);
  ccunit_addTestFixture (suite, fixtuer);
  result = ccunit_runTestSuite (suite, NULL);
  return 0;
}

TestSuites don't only have to contain TestFixtures . They can contain any object that implements the Test interface. For example, you can create a TestSuite (complex_add_sub_suite ()) in your code and I can create one in mine (complex_mul_div_suite ()), and we can run them together by creating a TestSuite that contains both:

  CCUnitTestSuite* suite;
  CCUnitTestResult* result;
  suite = ccunit_newTestSuite ("Complex add/sub/mul/div test suite");
  ccunit_addTestSuite (suite, complex_add_sub_suite ());
  ccunit_addTestSuite (suite, complex_mul_div_suite ());
  result = ccunit_runTestSuite(suite, NULL);

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

$ gcc -I. -o runTestSuite runTestSuite.c testComplex.c testComplexMulDiv.c complexTestSuite.c complex.c -lccunit
$ ./runTestSuite

TestRunner

How do you run your tests and collect their results? The fixtures store result of own tests into the TestResult .

inline_dotgraph_5.dot

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 runTestRunner.c:

#include <ccunit/CCUnitTestRunner.h>
#include <ccunit/CCUnitTestSuite.h>

And call to ccunit_runTestRunner (CCUnitTestRunner*, CCUnitTestSuite *) in the main() function:

extern CCUnitTestSuite* complex_add_sub_suite ();
extern CCUnitTestSuite* complex_mul_div_suite ();

int main( int argc, char **argv)
{
  CCUnitTestRunner* runner;
  CCUnitTestSuite* suite;
  suite = ccunit_newTestSuite ("complex test suite");
  ccunit_addTestSuite (suite, complex_add_sub_suite ());
  ccunit_addTestSuite (suite, complex_mul_div_suite ());
  runner = ccunit_newTestRunner (stdout);
  return ccunit_runTestRunner (runner, suite);
}

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

$ gcc -I. -o runTestRunner runTestRunner.c testComplex.c testComplexMulDiv.c complexTestSuite.c complex.c -lccunit
$ ./runTestRunner
.....
Time: 0.000066 sec

OK (5 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:

/* testComplex.c */
/** test equals */
void test_complex_equals ()
{
  CCUNIT_ASSERT_TEST_OBJ (s10_1, complex_equals, s10_1, complex_to_string);
/*CCUNIT_ASSERT_TEST_OBJ (s10_1, !complex_equals, s1_1, complex_to_string);*/
  CCUNIT_ASSERT_TEST_OBJ (s10_1, complex_equals, s1_1, complex_to_string);
}
$ gcc -I. -o runTestRunner runTestRunner.c testComplex.c testComplexMulDiv.c complexTestSuite.c complex.c -lccunit
$ ./runTestRunner
.F....
Time: 0.000059 sec

FAILURES!!!
Test Results: 
Run 5, Failures 1
There was 1 failure:
testComplex.c:53: complex equals test:
        complex_equals (s10_1, s1_1)
        expect: 10+1i
        actual: 1+1i

Helper Tool

As you might have noticed, implementing the suite () function of fixture is a repetitive and error prone task. A Creating TestSuite set of functions and command have been created to automatically implements the suite() function.

The following code is a rewrite of ComplexTest using those command:

First, you declare the fixture, passing the test fixture name to the javaDoc style comment, which consist of a C-style comment block starting with two *'s:

#include <ccunit/CCUnitAssert.h>

/** test case: complex number test */

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. Then, you define each test case of the fixture with prefix test, setUp, tearDown:

#include <complex.h>

static complex_t* s10_1;
static complex_t* s1_1;
static complex_t* s11_2;
static complex_t* sc;

void setup_setUp_complex_test ()
{
  sc = complex_new (1, 1);
}

void setup_tearDown_complex_test ()
{
  complex_delete (sc);
}

void setUp_complex_test ()
{
  s10_1 = complex_new (10, 1);
  s1_1 = complex_new (1, 1);
  s11_2 = complex_new (11, 2);
}

void tearDown_complex_test ()
{
  complex_delete (s10_1);
  complex_delete (s1_1);
  complex_delete (s11_2);
}

/** test equals */
void test_complex_equals ()
{
  CCUNIT_ASSERT_TEST_OBJ (s10_1, complex_equals, s10_1, complex_to_string);
  CCUNIT_ASSERT_TEST_OBJ (s10_1, !complex_equals, s1_1, complex_to_string);
}

/** test add */
void test_complex_add ()
{
  complex_t c10_1 = { 10.0, 1.0 };
  complex_t c1_1 = { 1.0, 1.0 };
  complex_t result;
  complex_t c11_2 = { 11.0, 2.0 };
  CCUNIT_ASSERT (complex_equals (&c11_2, complex_add (&result, &c10_1, &c1_1)));
}

/** test sub */
void test_complex_sub ()
{
  complex_t c9_0 = { 9, 0 };
  complex_t result;
  CCUNIT_ASSERT_TEST_OBJ (&c9_0, complex_equals,
                          complex_sub (&result, s10_1, s1_1),
                          complex_to_string);
}

Finally, you end the fixture declaration:

/** end test case */

To generate creating suite function code, run ccunit_makeSuite tool.

$ ccunit_makeSuite -f complex_suite -o suiteComplex.c testComplex.c
$ cat suiteComplex.c

#include <ccunit/CCUnitTestSuite.h>
#include <ccunit/CCUnitTestFixture.h>
#include <ccunit/CCUnitTestCase.h>

/* test fixture: complex number test */
/* setUp_complex_test */
extern void setUp_complex_test ();
/* tearDown_complex_test */
extern void tearDown_complex_test ();
/* test_complex_equals */
extern void test_complex_equals ();
/* test_complex_add */
extern void test_complex_add ();
/* test_complex_sub */
extern void test_complex_sub ();

static CCUnitTestFunc fx_001_cases[] = {
  {
    "test_complex_equals",
    "test equals",
    test_complex_equals
  },
  {
    "test_complex_add",
    "test add",
    test_complex_add
  },
  {
    "test_complex_sub",
    "test sub",
    test_complex_sub
  },
  {
    NULL, NULL, NULL
  },
};

static CCUnitTestFixtureDfn fx_001 = {
  { ccunitTypeFixture },
  "complex number test",
  {
    "setup_setUp_complex_test",
    "setup_setUp_complex_test",
    setup_setUp_complex_test
  },
  {
    "setup_tearDown_complex_test",
    "setup_tearDown_complex_test",
    setup_tearDown_complex_test
  },
  {
    "setUp_complex_test",
    "setUp_complex_test",
    setUp_complex_test
  },
  {
    "tearDown_complex_test",
    "tearDown_complex_test",
    tearDown_complex_test
  },
  fx_001_cases,
};

static CCUnitTestDfn* suite_001_test[] = {
    &fx_001.test,
    NULL,
};

static CCUnitTestSuiteDfn suite_001 = {
  { ccunitTypeSuite },
  "",
  suite_001_test
};


CCUnitTestSuite* complex_suite (const char* name)
{
  if (!suite_001.name[0])
    suite_001.name = name;
  return ccunit_newTestSuiteFromDfn (&suite_001);
}
$

Fixtures can be packaged into test suite. You declare the suite before fixtures.

/** test suite: complex number test suite */
/** test case: complex number equality test */
...
/** test case: complex number compute test */
...
 SOURCEFILE ::= [SUITE]... | FIXTURE...

 SUITE ::= SUITE_HEADER
           [ any-C-code ]...
           FIXTURE...
           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

 FIXTURE ::= FIXTURE_HEADER
             [FIXTURE_CODE]...
             [SETUP_SETUP_FUNC]
             [SETUP_TEARDOWN_FUNC]
             [SETUP_FUNC]
             [TEARDOWN_FUNC]
             [TESTCASE]...
             FIXTURE_END

 FIXTURE_HEADER ::= JAVADOC_COMMENT_BEGIN
                    'test case:' TESTFIXTURE_NAME
                    COMMENT_END
 FIXTURE_CODE ::= any C language codes.

 SETUP_SETUP_FUNC ::= [ FUNC_DESC ]
                        ['static'] 'void setup_setUp'[A-Za-z0-9_]* '()'
                        FUNC_BODY

 SETUP_TEARDOWN_FUNC ::= [ FUNC_DESC ]
                           ['static'] 'void setup_tearDown'[A-Za-z0-9_]* '()'
                           FUNC_BODY

 SETUP_FUNC ::= [ FUNC_DESC ]
                  ['static'] 'void setUp'[A-Za-z0-9_]* '()'
                  FUNC_BODY

 TEARDOWN_FUNC ::= [ FUNC_DESC ]
                     ['static'] 'void tearDown'[A-Za-z0-9_]* '()'
                     FUNC_BODY

 TESTCASE ::= [ FUNC_DESC ] 'void test'[A-Za-z0-9_]* '()' FUNC_BODY

 FIXTURE_END ::= JAVADOC_COMMENT_BEGIN 'end test case' wsp string COMMENT_END

 FUNC_DESC ::= JAVADOC_COMMENT_BEGIN string COMMENT_END

 FUNC_BODY ::= '{' C language codes... '}'

 JAVADOC_COMMENT_BEGIN ::= '/' '*' '*'
 COMMENT_END ::= '*' '/'

Post-build check

Now that we have our unit tests running, how about integrating unit testing to our build process ?

To do that, the application must returns a value different than 0 to indicate that there was an error.

ccunit_runTestRunner() returns a integer indicating if the run was successful.

Updating our main programm, we obtains:

#include <ccunit/CCUnitTestRunner.h>

extern CCUnitTestSuite* complex_suite(const char* name);

int main (int argc, char** argv)
{
  CCUnitTestRunner* runner;
  CCUnitTestSuite* suite;
  int wasSucessful;
  runner = ccunit_newTestRunner (stdout);
  suite = complex_suite ("complex test suite");
  wasSucessful = ccunit_runTestRunner (runner, suite);
  return wasSucessful;
}

Now, you need to run your application after compilation.

$ gcc -I. -o runTest runTest.c testComplex.c suiteComplex.c complex.c -lccunit
$ ./runTest
...
Time: 0.000032 sec

OK (3 tests)
$ ccunit_makeSuite -f complex_suite -o suiteComplex.c testComplex.c testComplexMulDiv.c
$ gcc -I. -o runTest runTest.c testComplex.c testComplexMulDiv.c suiteComplex.c complex.c -lccunit
$ ./runTest
.....
Time: 0.000045 sec

OK (5 tests)

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

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