Webmaster  |  Imprint 
Platinum
Platinum C++ Framework
Main  |  License  |  Documentation  |  Download  |  Support 

Unit Testing

A Simple Test Case

The TestCase class can be used to implement simple Unit test. This is simply done by deriving from TestCase and implementing the TestCasetest method. The member functions TestCasesetUp and TestCasetearDown can be used to manage any resources the test might require. The PT_UNIT_ASSERT macro can be used to assert test conditions during the test.

class MyTestCase : public Pt::Unit::TestCase
{
    public:
        MyTestCase()
        : Pt::Unit::TestCase("MyTestCase")
        , _a(0), _b(0)
        { }

        void setUp()
        {
            _a = 5;
            _b = 5;
        }

        void tearDown()
        {
        }

        void test()
        {
            _a += _b;
            PT_UNIT_ASSERT(_a == 10);
        }

    private:
        int _a;
        int _b;
};

Build System Integration

The Jamrules.Unit rule set containes rules to build a Unit test and have it automatically run after it has been build. Instead of the rule 'Main' the rule 'PtUnitTest' has to be used in a Jamfile:

PtUnitTest mytest : mytest.cpp ;

This will cause the jam tool to report a failed build if the Unit test does not run through, which is the desired behaviour. This way Unit tests can easily be integrated in automated builds.

Data Driven Testing

Sometimes it is desirable to repeat a test with different data. This is achieved by registering methods that take arguments (the test data) in a TestSuite.

class MyTestSuite
{
    public:
        MyTestSuite()
        : Pt::Unit::TestSuite("MyTestSuite")
        {
            this->registerMethod("AdditionTest", *this, &MyTestSuite::AdditionTest);
        }

        void AdditionTest(int a, int b)
        {
            // testing code
        }
};

A protocol can then by used to call the AdditionTest method multiple times with different data. For convenience, such a protocol is included in the Unit test module already and is called TestSchedule. Here we can assign data to tests.

class MyTestSchedule : public Pt::Unit::TestSchedule
{
    public:
        MyTestSchedule()
        , _first(1, 1)
        , _second(2, 2)
        , _three(3, 3)
        {
            this->includeTest("AdditionTest", _first);
            this->includeTest("AdditionTest", _second);
            this->includeTest("AdditionTest", _third);
        }

    private:
        Args _first;
        Args _second;
        Args _third;
};

When this protocol is applied to MyTestSuite, the AdditionTest will be called three times with different test data. It is very possible to read data from a file or another data source.

Protocol Driven Testing

Protocol driven testing implements the idea to control the order in which tests are executed, or to run test methods multiple times. To accomplish this a protocol is defined by deriving from the TestProtocol class.The following example will run a test called 'MyTest' three times and sleeps between the tests for 1 second.

#include <Pt/Unit/TestProtocol.h>
#include <Pt/System/Process.h>

class MyProtocol : public Pt::Unit::TestProtocol
{
    public:
        MyProtocol()
        {}

        void run(Pt::Unit::TestSuite& suite)
        {
            suite.runTest( "MyTest" );
            Pt::System::Process::sleep(1000);
            suite.runTest( "MyTest" );
            Pt::System::Process::sleep(1000);
            suite.runTest( "MyTest" );
        }
};

The protocol can then be applied to a TestSuite. The methods are resolved using object reflection. The TestSuite class requires that all runnable tests are registered for reflection. TestSuite inherits reflection capabilities from Pt::Reflectable. If a test can not be executed through the TestSuite::runTest method an exception of the type PtLogicError is thrown and the test fails if it is allowed to propagate. The new protocol can be assigned to a TestSuite in the constructor:

MyProtocol protocol;

class MyTestSuite
{
    public:
        MyTestSuite()
        : Pt::Unit::TestSuite("MyTestSuite", protocol)
        {
            this->registerMethod("MyTest", *this, &MyTestSuite::MyTest);
        }

        void MyTest()
        {
            // testing code
        }
};

Alternatively, a protocol can be set using the Unit::TestSuite::setProtocol method. It is entirely possible to load and assign protocols at run-time.

Running Tests

All tests can be run by the Application object of the Unit module. To register a test with the application object the RegisterTest class template can be used or Application::registerTest can be called. A typical test program will instanciate an Application object and set reporters for result reporting and logging.

int main()
{
    Pt::Unit::Application app;
    std::ofstream fs("log.txt");
    Pt::Unit::Reporter reporter(fs);
    app.setReporter(reporter);
    return app.run();
}

For convenience, the header file TestMain.h already contains such a main loop where reporters can be selected by command line arguments. So the implementor of a test only has to include TestMain.h in the file where be derives and registers the tests.

Copyright © 2003-2007 The Pt Development Team
Pt 1.0