img snowphy

HowTo for CppUnit TestFramework

发表于2004/10/22 10:31:00  1338人阅读

分类: 技术文章

LCG Application Area - LCG Infrastructure: SW - Testing

HowTo for CppUnit TestFramework

What is CppUnit?
How to start testing with CppUnit
How to use CppUnit in LCG AppArea
Related information

What is Cppunit?

CppUnit is a framework for writing unit tests in C++ and running them automatically, giving a report about success or failing tests. It is the C++ port of the famous JUnit framework for unit testing. CppUnit is one of the members of the "XUnit family" (JUnit, PerlUnit, PyUnit, QtUnit, ...) test frameworks free available from XP software. The test output is in XML or in text format for automatic testing and GUI based for supervised tests.
The first port of JUnit to C++ was done by Michael Feathers. His versions can be found on the XProgramming software page. They are os-specific, so Jerome Lacoste provided a port to Unix/Solaris. His version can be found on the same page. The CppUnit project has combined and built both on this work. The main purpose of CppUnit is to support developers in doing their unit testing of C++ programs.

CppUnit works in Linux, Solaris and Windows platforms. See the SPI supported platforms+compilers at SPI external software service.

return to top of page

How to start testing with Cppunit

Installation check

To use CppUnit the CppUnit libraries and include files should be installed or accessible to your machine. To check this point in a linux/unix machine you can type at the prompt

    $ cppunit-config [--version] [-cflags] [--libs] [--prefix] [--help] 
Using the different optional flags you can inquire the system about the CppUnit installed version, the cflags and library path you have to use, where CppUnit was installed and this help.
If you are using one of the initial shell scripts provided by
SPI in the general HowTo for Test Execution Frameworks all the needed information is loaded in the environment variables ($PATH, $LD_LIBRARY_PATH, $CPPUNIT_LDFLAGS, $CPPUNIT_INCLUDES, $ACLOCAL) that you can check at the prompt with the echo command (echo $PATH, ...).
If you are working in a SCRAM project, CppUnit should appear as external tool. Type "scram tool list" at the prompt and check it. You can also use "scram tool info cppunit" to know more about the CppUnit installed release.

If your development platform is Windows and Visual C++ you should check that all the steps described in HowTo for Test Execution Frameworks for the integration of the CppUnit in a windows platform with/without DFS access were well done.

What to read about the usage of CppUnit

- The CppUnit Cookbook written by Michael Feathers can give you a first idea of what is a "Fixture" , a "Test Case", a "Test Suite", the "TestRunner" and the "TestFactoryRegistry". For background information on the basic design of the framework the reader is referred to Kent's original paper, "Simple Smalltalk Testing: With Patterns".

- The section "Using CppUnit" from Crash Course in using CppUnit document can also help.

- But maybe the easy way to start the sw testing with CppUnit is following the examples provided by SPI. These are done using CppUnit Helper-Macros and in this way the work of the developer is reduced. A unique test driver main program (placed at the CppUnit include path) is able to handle all CppUnit tests classes in the test directory.

How to jump into the examples

If you are really impatient to start the testing job you can take a look at a set of basic examples which you can download from the SPI repository. Unzip and un-tar the "Testing_examples.tar.gz" file. Take a look of the README file. It explains where are the CppUnit examples and how to run them in the different platforms (Linux/Solaris/Windows).

return to top of page

How to use CppUnit in LCG AppArea

  1. Check that "cppunit-config" is in your path or that Cppunit is configured as an external tool in your SCRAM project. (See installation check item in this document).
  2. Make sure that in your CppUnit include path you have the CppUnit test driver "CppUnit_testdriver.cpp". It always looks like this one:
    #include <cppunit/extensions/TestFactoryRegistry.h>
    #include <cppunit/ui/text/TestRunner.h>
    #include <cppunit/CompilerOutputter.h>
    #include <cppunit/TextOutputter.h>
    #include <cppunit/XmlOutputter.h>
    #include <iostream>
    /**  Main class for all the CppUnit test classes  
    *  This will be the driver class of all your CppUnit test classes.
    *  - All registered CppUnit test classes will be run. 
    *  - You can also modify the output (text, compiler, XML). 
    *  - This class will also integrate CppUnit test with Oval
    int main( int argc, char **argv)
       /// Get the top level suite from the registry
       CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest();
       /// Adds the test to the list of test to run
       CppUnit::TextUi::TestRunner runner;
       runner.addTest( suite );
       // Change the default outputter to a compiler error format outputter 
       // uncomment the following line if you need a compiler outputter.
          runner.setOutputter(new CppUnit::CompilerOutputter( &runner.result(),
                                                             std::cout ) );
       // Change the default outputter to a xml error format outputter 
       // uncomment the following line if you need a xml outputter.
       //runner.setOutputter( new CppUnit::XmlOutputter( &runner.result(),
       //                                                    std::cerr ) );
       /// Run the tests.
           bool wasSuccessful = runner.run();
       // If you want to avoid the CppUnit typical output change the line above 
       // by the following one: 
       //  bool wasSucessful = runner.run("",false,false,false);
       // Return error code 1 if the one of test failed.
       // Uncomment the next line if you want to integrate CppUnit with Oval
          if(!wasSuccessful !=0) std::cerr <<"Error: CppUnit Failures"<< std::endl;
          std::cout <<"[OVAL] Cppunit-result ="<< !wasSuccessful<<"/n" ;
    This test driver will look for all your registered CppUnit test classes and will run all of them providing and text/compiler/XML output. If all the tests pass, you will get an informative message. If any fail, you will get the following information:
    • The name of the test case that failed.
    • The name of the source file that contains the test.
    • The line number where the failure occurred
    • All of the text inside the call to CPPUNIT_ASSERT() which detected the failure
    The last line of this example is used to integrate the CppUnit tests in the Oval Test FrameWork. When you call Oval ( "oval run test_Package_X_test1" and "oval diff test_Package_X_test1") if any of the CppUnit tests fails Oval will receive an alert.
    Note: Here we are following the naming conventions define in the HowTo Make Sw-Tests document. Please follow them.
  3. Edit your first CppUnit test class, you can start with one like this:
    * CppUnit test class: test_Package_X_test1.cpp 
    * Use the HelperMacros.h header file to establish in a easy 
    * way the test suite in which you can place all the tests 
    * concerning the tested class or component.
    #include <cppunit/extensions/HelperMacros.h>
    class MyClass_test : public CppUnit::TestFixture{
    /// /brief  Definition of the unit test suite "MyClass_test ",
    /// don't forget to add all those tests("test1",
    /// "test2"... ) you would like to run. 
      CPPUNIT_TEST_SUITE( MyClass_test );
      CPPUNIT_TEST( test1 );
      CPPUNIT_TEST( test2 );
      int alpha;
      int beta;
    /// /brief Override setUp() to initialize the 
    ///        variables you will use for test
      void setUp () {
    /// Write now your own tests, this is the first one
       void test1() {
         int a = 10;
         int b(10);
         CPPUNIT_ASSERT( a == b );
    /// Write now your own tests, this is the first one
       void test2() {
         float tol = 0.05;
         float a = 10.;
         float b = a + tol/10.;
         CPPUNIT_ASSERT_DOUBLES_EQUAL( a, b, tol );
    // Final clean up
      void tearDown() {
    // Class registration on cppunit framework
    /// /brief You have to register the test suite "ComplexNumberTest". In this 
    /// it will be recognized by the may test program which drives the 
    /// different tests in a specific package test directory.
    // CppUnit test-driver common for all the cppunit test classes 
    #include <CppUnit_testdriver.cpp>

    Note: If you don't like to type you can use the script lcg-distributeTest.py and you will receive all the stuff for testing with CppUnit and other test-frameworks.

    At this point you should be able to compile "test_Package_X_test.cpp" with your own MakeFile or through SCRAM (scram b), produce the executable "test_Package_X_test" and run it with the following result:

    $> test_Package_X_test
    OK (2 tests)
    [OVAL] Cppunit-result =0
    The output says that there are 2 cppunit tests and no failures. The "[OVAL] Cppunit-result =0" tag is intended for the integration of CppUnit and Oval.
  4. Now you can write your new test methods or if you prefer you can package them into another CppUnit test class like this we discussed but without the last line including the Cppunit test driver. The new test suite you defined is added to the test suite defined in the previous step. In any case remember what is required by CppUnit:
    • Include the CppUnit Helper Macros: #include <cppunit/extensions/HelperMacros.h>
    • Your test class must be derived publically from CppUnit::TestFixture
    • Define your test suite and give a name using CPPUNIT_TEST_SUITE( MyTestSuiteName );
    • Add as many tests as you want to this test-suite using CPPUNIT_TEST( myfirsttest ); and end the suite declaration with CPPUNIT_TEST_SUITE_END();
    • Use the methods setUp() and tearDown() to set and to release any permanent resources you allocate for the test-suite
    • Write your tests using the CppUnit macros for making assertions:
      • CPPUNIT_ASSERT(condition)
        Assertions that a condition is true.
      • CPPUNIT_ASSERT_MESSAGE(message, condition)
        Assertion with a user specified message.
      • CPPUNIT_FAIL(message)
        Fails with the specified message
      • CPPUNIT_ASSERT_EQUAL(expected, actual)
        Asserts that two values are equals.
      • CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expected, actual)
        Asserts that two values are equals, provides additional message on failure
      • CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, delta)
        Macro for primitive value comparisons.
    • Don't forget to register your test suite with CPPUNIT_TEST_SUITE_REGISTRATION(MyTestSuiteName).
    Once more remember, why do you need unit testing?:
    • Before writing code, it forces you to detail your requirements in a useful way
    • While writing code, it keeps you from over-coding. When all the test cases pass, the function is complete.
    • When refactoring code, it assures you that the new version behaves the same way as the old version.
    • When maintaining code, it helps you cover yourself when someone comes screaming that your latest change broke their old code.

    return to top of page

    LCG CppUnit FAQ

    1.- How can I check if my test throws an exception?
    The CppUnit helper macros also help you to check the exceptions. For this:

    • Add the test of your exception to the test suite using CPPUNIT_TEST_EXCEPTION (testMethod, ExceptionType)and specifying the expected exception type. For example:
      CPPUNIT_TEST_EXCEPTION(testVectorAtThrow, std::invalid_argument);
    • Write the test case method testMethod
       void testVectorAtThrow()
           std::vector v(2);
           v.at(1);     // this will not throw exception std::invalid_argument
    CppUnit will answer with the following failure:
      1)test: ExampleTestCase.testVectorAtThrow (F) 
     "Expected exception of type Std_invalid_argument, but got none"

    2.- How can I do test for failure?
    It is not enough to test that our functions succeed when given good input; we must also test that they fail when given bad input. And not just any sort of failure; they must fail in the way we expect. To do this:

    • Add the test which will fail to the test suite using CPPUNIT_TEST_FAIL(testMethod). For example:
      CPPUNIT_TEST_FAIL( testAssertFalseFail );
    • Write the test case that should fail:
      void testAssertFalseFail()
        CPPUNIT_ASSERT( 1 == 0 );

    return to top of page

    Related information

    Useful Links:

    return to top of page
    LCG AppArea SPI support
    Last modified: Tue Sep 16 12:26:57 CEST 2003
    $Revision: 1.9 $ $Author: mgallas $
    0 0


    取 消