/* -*- Indented-Text -*- */
/* $Id$ */
/** @page cookbook CCUnit Cookbook
Here is a short cookbook to help you get started.
@section simple_test_case 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:
- Create a test function
- When you want to check a value, call
@link CCUNIT_ASSERT() CCUNIT_ASSERT(bool) @endlink
and pass a bool that is true if the test succeeds
For example, to test that the sum of two complex number
which is the sum of the values of two complex numbers,
write:
@code
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)));
}
@endcode
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.
@section fixture Fixture
What if you have two or more tests that operate on the same
similar set of objects?
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 sevral
different tests. Each case will send slightly different
messages or parameteres to the fixture and will check for
different results.
When you have a common fixture, here is what you do:
- Create a @link CCUnitTestCase TestCase @endlink object
- Add an global variable for each part of the fixture
- Write
setUp()
function to initialize the valiables
- Write
tearDown()
to release any permanent
resources you allocated in setUp
- Create a @link CCUnitTestFixture TestFixture @endlink object
- Add test case objects to fixture object
For example, to write several test cases, first create a
fixture:
@code
//** TEST CASE: complex number test *\/
#include "complex.h"
static complex_t* s10_1;
static complex_t* s1_1;
static complex_t* s11_2;
void setUp_ComplexTest ()
{
s10_1 = complex_new (10, 1);
s1_1 = complex_new (1, 1);
s11_2 = complex_new (11, 2);
}
void tearDown_ComplexTest ()
{
complex_delete (s10_1);
complex_delete (s1_1);
complex_delete (s11_2);
}
...
CCUnitTestFixture* fixture;
fixture = ccunit_newTestFixture ("ComplexTest",
CCUNIT_NEWTESTFUNC(setUp_ComplexTest),
CCUNIT_NEWTESTFUNC(tearDown_ComplexTest));
@endcode
Once you have the Fixture in place, you can write as complex
Test Cases as you'd like.
@section test_case 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:
@code
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_addNewTestCase (fixture,
"test_complex_equals",
"complex equals test",
test_complex_equals);
ccunit_addNewTestCase (fixture,
"test_complex_add",
"complex add test",
test_complex_add);
@endcode
One may create and run objects for each test case like this:
@code
CCUnitTestResult* result;
result = ccunit_runTestFixture (fixture);
@endcode
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
@link ExecutingTest TestRunner @endlink (see below) to
display the results.
Once you have several tests, organize them into a suite.
@section suite Suite
How do you set up your tests so that you can run them all at once?
%CCUnit provides a @link CCUnitTestSuite TestSuite @endlink
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:
@code
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);
@endcode
@link CCUnitTestSuite TestSuites @endlink don't only have to
contain @link CCUnitTestFixture TestFixtures @endlink. They
can contain any object that implements the @link CCUnitTest
Test @endlink interface. For example, you can create a
@link CCUnitTestSuite TestSuite @endlink in your code and I
can create one in mine, and we can run them together by
creating a @link CCUnitTestSuite TestSuite @endlink that
contains both:
@code
CCUnitTestSuite* suite;
CCUnitTestResult* result;
suite = ccunit_newTestSuite ("suite");
ccunit_addTestSuite (suite, complex_add_sub_suite ());
ccunit_addTestSuite (suite, complex_mul_div_suite ());
result = ccunit_runTestSuite(suite, NULL);
@endcode
@section test_runner TestRunner
How do you run your tests and collect their results?
Once you have a test suite, you'll want to run it. %CCUnit
provides tools to define the suite to be run and to display
its results. You make your suite accessible to a @link
CreatingTestSuite ccunit_makeSuite @endlink tool that generate a
creating test suite code.
For example, to make a ComplexTest suite available to a
@link CreatingTestSuite ccunit_makeSuite @endlink,
excute the following tool to
ComplexTest.c:
@code
$ ccunit_makeSuite -f complex_suite -o suiteComplex.c ComplexTest.c
@endcode
@anchor test_runner_code
To use the TestRunner, include the header files for the tests in Main.c:
@code
#include
#include
@endcode
And call to
@link ccunit_runTestRunner()
ccunit_runTestRunner (CCUnitTestRunner*, CCUnitTestSuite *) @endlink
in the main()
function:
@code
extern CCUnitTestSuite* complex_suite(const char* name);
int main( int argc, char **argv)
{
CCUnitTestRunner* runner;
CCUnitTestSuite* suite;
runner = ccunit_newTestRunner (stdout);
suite = complex_suite ("complex test suite");
return ccunit_runTestRunner (runner, suite);
}
@endcode
The @link ExecutingTest TestRunner @endlink will run the tests.
If all the tests pass, you'll get an informative message.
If any fail, you'll get the following information:
- The name of the source file that contains the test
- The line number where the failure occurred
- The name of the test case that failed
- All of the text inside the call to
CCUNIT_ASSERT ()
which detected the failure
@section helper_macros Helper Tool
As you might have noticed, implementing the fixture
suite ()
function is a repetitive and error
prone task. A @ref CreatingTestSuite set of functions and
command have been created to automatically implements the
suite()
method.
The following code is a rewrite of ComplexTest using those command:
@code
#include
@endcode
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:
@code
//** test case: complex number test *\/
@endcode
The suite created by the ccunit_suite()
function is specified -f
option of command
ccunit_makeSuite
. Then, you define each test
case of the fixture with prefix test
,
setUp
, tearDown
:
@code
#include
static complex_t* s10_1;
static complex_t* s1_1;
static complex_t* s11_2;
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);
}
@endcode
Finally, you end the fixture declaration:
@code
//** end test case *\/
@endcode
To generate creating suite function code, run
ccunit_makeSuite
tool.
@code
$ ccunit_makeSuite testComplex.c
#include
#include
#include
//* 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 CCUnitTestFixtureDfn fx_001 = {
{ ccunitTypeFixture },
"complex number test",
{
"setUp_complex_test",
"setUp_complex_test",
setUp_complex_test
},
{
"tearDown_complex_test",
"tearDown_complex_test",
tearDown_complex_test
},
{
{
"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 CCUnitTestSuiteDfn suite_001 = {
{ ccunitTypeSuite },
"",
{
&suite_002.test,
NULL,
},
};
CCUnitTestSuite* ccunit_suite (const char* name)
{
if (!suite_001.name[0])
suite_001.name = name;
return ccunit_newTestSuiteFromDfn (&suite_001);
}
$
@endcode
@section post_build_check 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.
@link ccunit_runTestRunner() ccunit_runTestRunner() @endlink returns
a integer indicating if the run was successful.
Updating our main programm, we obtains:
@code
#include
int main (int argc, char** argv)
{
CCUnitTestRunner* runner;
CCUnitTestSuite* suite;
int wasSucessful;
runner = ccunit_newTestRunner (stdout);
suite = ccunit_suite ();
wasSucessful = ccunit_runTestRunner (runner, suite);
return wasSucessful;
}
@endcode
Now, you need to run your application after compilation.
*/