Search code examples
c++cppunit

How to use cppunit just like junit --- two mains


I know that the unit test for Java is very simple. We just need use JUnit and run test classes as JUnit test.

Now I'm working with C++ and I find a test library: cppunit.

It seems that I need to run my test with a specific function main. However, a cpp project can have ONLY ONE main.

What should I do? I have to switch mains when I do my test and when I run my project?


Solution

  • What should I do? I have to switch mains when I do my test and when I run my project?

    You should separate the code to test into a library project, that can be linked from your target application and the unit test runner.

    Then have two more projects providing a main() function:

    • one for the target app, that just forwards main() implementation to a call of a MyTargetApp class member function, e.g. MyTargetApp::run()
    • one for the unit testing, that calls a TestRunner, and contains all of the test suite and fixture classes (including these for the MyTargetApp).

    The target application project can be configured without linking against the cppunit library and your test suite/fixture implementations (which will certainly reduce the final artifacts footprint).


    However, a cpp project can have ONLY ONE main.

    Another option is to use just one project with a narrow main() function that can be compiled using a conditional preprocessor statement (as from their example on the TestRunner class):

     #ifdef TESTING
     int runUnitTests(int argc, char* argv[]);
     #endif
    
     int main(int argc, char* argv[]) {
    
     #ifdef TESTING
         // run the unit tests
         // -----------------------------------------------------------------
         return runUnitTests(argc,argv);
     #else
         // run the application
         // -----------------------------------------------------------------
         MyTargetApp app;
         return app.run(argc,argv);
     #endif
    

     #ifdef TESTING
     int runUnitTests(int argc, char* argv[]) {
       std::string testPath = (argc > 1) ? std::string(argv[1]) : "";
    
       // Create the event manager and test controller
       CppUnit::TestResult controller;
    
       // Add a listener that colllects test result
       CppUnit::TestResultCollector result;
       controller.addListener( &result );        
    
       // Add a listener that print dots as test run.
       CppUnit::TextTestProgressListener progress;
       controller.addListener( &progress );      
    
       // Add the top suite to the test runner
       CppUnit::TestRunner runner;
       runner.addTest( CppUnit::TestFactoryRegistry::getRegistry().makeTest() );   
       try {
         std::cout << "Running "  <<  testPath;
         runner.run( controller, testPath );
    
         std::cerr << std::endl;
    
         // Print test in a compiler compatible format.
         CppUnit::CompilerOutputter outputter( &result, std::cerr );
         outputter.write();                      
       }
       catch ( std::invalid_argument &e ) {
         // Test path not resolved
         std::cerr  <<  std::endl  
                    <<  "ERROR: "  <<  e.what()
                    << std::endl;
         return 0;
       }         
    
       return result.wasSuccessful() ? 0 : 1;
     }
     #endif
    

    Then have 2 different project configurations, one that defines TESTING and one without.

    One disadvantage of this solution is though, you'll get all the unit test suites and fixtures left in your program, because cppunit macros will register and instantiate them automatically, regardless if the will be called from the execution path.