Search code examples
c++cppunit

CppUnit - Registry multiple suites to be runned in a single main function


I am starting with C++ unit tests using CppUnit. The goal I am trying to achieve is to run all my test suites on a single main file (and function). Let me explain:

Let's say we have two classes with test for two C++ classes I have designed:

#ifndef FIRSTCLASSTEST_H
#define FIRSTCLASSTEST_H

class FirstClassTest : public CppUnit::TestFixture {

  CPPUNIT_TEST_SUITE( FirstClassTest );
  CPPUNIT_TEST( testConstructor );
  CPPUNIT_TEST_SUITE_END();

public:
  void setUp(void);
  void tearDown(void);
protected:
  void testConstructor(void);
};

#endif

And let the other class be similar:

#ifndef SECONDCLASSTEST_H
#define SECONDCLASSTEST_H

class SecondClassTest : public CppUnit::TestFixture {

  CPPUNIT_TEST_SUITE( SecondClassTest );
  CPPUNIT_TEST( testConstructor );
  CPPUNIT_TEST_SUITE_END();

public:
  void setUp(void);
  void tearDown(void);
protected:
  void testConstructor(void);
};

#endif

And the cpp files look similar to:

#include "FirstClassTest.h"

CPPUNIT_REGISTRY_ADD_TO_DEFAULT( FirstClassTest );

void FirstClassTest::setUp(){
   //setup...
}

void FirstClassTest::tearDown(){
  //teardown...
}

void FirstClassTest::testConstructor(){
   //some asserts
}

After these files (Which work properly individually), I want to create a unique file to run all my registered suites. Let's say my main test runner is AllTest.cpp:

#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/BriefTestProgressListener.h>
#include <cppunit/TestRunner.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/extensions/HelperMacros.h>

//Include Test classes here
#include "FirstClassTest.cpp"
#include "SecondClassTest.cpp"

int main()
{
  // informs test-listener about testresults
  CPPUNIT_NS::TestResult testresult;
  // register listener for collecting the test-results
  CPPUNIT_NS::TestResultCollector collectedresults;
  testresult.addListener (&collectedresults);

  // register listener for per-test progress output
  CPPUNIT_NS::BriefTestProgressListener progress;
  testresult.addListener (&progress);

  // insert test-suite at test-runner by registry
  CPPUNIT_NS::TestRunner testrunner;
  testrunner.addTest (CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest ());
  testrunner.run(testresult);

  // output results in compiler-format
  CPPUNIT_NS::CompilerOutputter compileroutputter(&collectedresults, std::cerr);
  compileroutputter.write ();

  // return 0 if tests were successful
  return collectedresults.wasSuccessful() ? 0 : 1;

}

But this does not work. How can I achieve the desired behavior? I have checked the docs: http://cppunit.sourceforge.net/doc/cvs/class_auto_register_suite.html but without success.

I suspect the main problem is in the AllTests.cpp and I am not adding the suites properly to the registry or to the testRunner.

Any help would be appreciated. Cheers.

SOLUTION EDIT: Finally, and thanks to the examples and advices provided by @moggi, I have been able to resolve my problem. I have removed any CPP_UNIT_SUITE_REGISTRATION from my cpp files and I have modified the AllTest.cpp main function to be like this:

int main()
{
  // informs test-listener about testresults
  CPPUNIT_NS::TestResult testresult;
  // register listener for collecting the test-results
  CPPUNIT_NS::TestResultCollector collectedresults;
  testresult.addListener (&collectedresults);

  // register listener for per-test progress output
  CPPUNIT_NS::BriefTestProgressListener progress;
  testresult.addListener (&progress);

  // insert test-suite at test-runner by registry
  CPPUNIT_NS::TestRunner testrunner;

  // MODIFIED PART, TEST SUITES ADDED MANUALLY
  testrunner.addTest( FirstClassTest::suite() );
  testrunner.addTest( SecondClassTest::suite() );

  testrunner.run(testresult);

  // output results in compiler-format
  CPPUNIT_NS::CompilerOutputter compileroutputter(&collectedresults, std::cerr);
  compileroutputter.write ();

  // return 0 if tests were successful
  return collectedresults.wasSuccessful() ? 0 : 1;

}

Solution

  • So there are basically two solutions that you use.

    1. Link everything together and register your test suite with CPPUNIT_TEST_SUITE_REGISTRATION and be happy. This requires the test code and your AllTest.cpp to be linked together.

    2. Dynamically load your tests during runtime and use CPPUNIT_PLUGIN_IMPLEMENT and also register with CPPUNIT_TEST_SUITE_REGISTRATION.

    The advantage of the first approach is that it is simple and you don't need to worry too much. Of course this simplicity comes with the price that if you want to use the code from AllTest.cpp for several independent tests that are not in separate binaries/libraries you need to link AllTest.cpp into all of them. More information about the CPPUNIT_TEST_SUITE_REGISTRATION can be found at the Creating TestSuite page in the documentation

    To implement this approach in your code add to your cpp file the following for each test suite:

    CPPUNIT_TEST_SUITE_REGISTRATION(FirstClassTest);
    

    Obviously replace FirstClassTest with the test suite name that you used in CPPUNIT_TEST_SUITE.

    The second approach is a bit more complicated but allows to have one test binary and dynamically load the library with the test code during runtime. This allows e.g. to build several independent tests that can be run in parallel with the test runner being shared. More information for this approach can be found at the cppunit documentation for the PluginIn approach