Using CppUnit's MFCTestRunner with VS 6.0

Richard M. Conlan - August 2005

This guide is walks you through getting MFCTestRunner working in Microsoft Visual Studio 6.0. I was reliant upon and am thankful for the existence of Axel Pospischil's roughly equivelent guide Checklist CppUnit and VC 6.0. I decided to make this guide despite the existence of Axel's because Axel's guide 1) has some minor errors, 2) glosses over a few points, and 3) uses screenshots from the German edition of MSVS6.

I used Microsoft Visual Studio 6.0 Professional w/ SP6 and CppUnit 1.10.2. To simplify things I am building a Debug configuration. If for some reason you want to target one of the other configurations you may need to make minor changes to these directions.

In the following I will assume that $CPPUNIT$ is the root directory of CppUnit. In my case this is D:\CppUnit v1.10.2. Note that the space in the directory name caused some problems, so you might want to avoid spaces.

You can download this zip if you want to use it to follow along:

CppUnitGUIDemo.zip

1. Get CppUnit

The first thing you have to do is download CppUnit, which is available here. I used 1.10.2, but you can likely use a later version.

2. Build CppUnit Libraries

This isn't as hard as it sounds. If you're interested in the gritty details open $CPPUNIT$\INSTALL-WIN32.txt, but basically you just:
  1. Open the $CPPUNIT$\src\CppUnitLibraries.dsw workspace in VC++
  2. In the 'Build' menu, select 'Batch Build...'
  3. In the batch build dialog, select all projects and press the Build button
  4. The resulting libraries can be found in the $CPPUNIT$\lib directory
NOTE: If you want to save some time I recommend only building the Debug versions of the libraries as that is all we'll be using here. (Unless for some reason you want to build the project as a Release, in which case you'll need to build the Release versions of the CppUnit libraries.)

3. Create A New 'MFC AppWizard (exe)' Project

  1. Select File -> New from the menu bar
  2. Select 'Projects' tab
  3. Select 'MFC AppWizard (exe)' project
  4. Project name = 'CppUnitGUIDemo'
  5. Select 'Dialog based' as the type of application to create
  6. I kept resources in English, but that may not matter
  7. Uncheck 'About box' and 'ActiveX Controls'
  8. Select 'MFC Standard' as the style of project
  9. Select 'Yes, please' for source comments
  10. Select 'As a shared DLL' for the MFC library

4a. Enable Run-Time Type Information (RTTI)

  1. Select Project -> Settings from the menu bar
  2. Select the C/C++ tab
  3. Select C++ Language from the Category dropbox
  4. Check the 'Enable Run-Time Type Information (RTTI)' checkbox

4b. Select The Appropriate Run-Time Library

  1. Select Code Generation from the Category dropbox
  2. Select 'Debug Multithreaded DLL' from the 'Use run-time library:' dropbox

4c. Turn Off Precompiled Headers

  1. Select Precompiled Headers from the Category dropbox
  2. Select 'Not using precompiled headers'



NOTE:

4d. Set The Preprocessor Include Path

  1. Select Preprocessor from the Category dropbox
  2. Enter $CPPUNIT$\include in the 'Additional include directories:' box

5. Link To The Appropriate CppUnit/TestRunner Libraries

  1. Select the Link tab
  2. Select General from the Category dropbox
  3. Enter $CPPUNIT$\lib\cppunitd.lib and $CPPUNIT\lib\testrunnerd.lib in the 'Object/library modules:' box


NOTE:

6a. Update Visual Studio's Include Path

  1. Select Tools -> Options from the menu bar
  2. Select the Directories tab
  3. Select 'Include files' from the 'Show directories for:' dropbox
  4. Add $CPPUNIT$\include to the 'Directories' list

6b. Update Visual Studio's Include Path

  1. Select 'Library files' from the 'Show directories for:' dropbox
  2. Add $CPPUNIT$\lib to the 'Directories' list

6c. Update Visual Studio's Source Path

  1. Select 'Source files' from the 'Show directories for:' dropbox
  2. Add $CPPUNIT$\src\cppunit to the 'Directories' list

7. Make Sure testrunnerd.dll Is In Your PATH

There are a few ways to do this. One it to just put testrunnerd.dll in your environment PATH. Another is to manually copy it to the build folder after you build but before you try to execute your test program. For me, the easiest was to employ Visual Studio's Post-Build step to have it automatically copied whenever I built the project:
  1. Select Project -> Settings from the menu bar
  2. Select the Post-build step tab
  3. Enter 'Copying TestRunner DLL To Build Folder' in the 'Post-build description' box
  4. Add 'copy "D:\CppUnit v1.10.2\lib\testrunnerd.dll" $(OutDir)' to the 'Post-build command(s)' list


NOTE:

8. Edit Main Application File

Add the code between the "// +++++++++++++++++++++++++" comments to your main MFC application file. For my project this is CppUnitGUIDemo.cpp.


#include "stdafx.h"
#include "MainApp.h"

[...] // other includes

// +++++++++++++++++++++++++
    // CppUnit: MFC TestRunner
    #include <cppunit/ui/mfc/TestRunner.h>

    // CppUnit: TestFactoryRegistry to retreive the top test
    // suite that contains all registered tests.
    #include <cppunit/extensions/TestFactoryRegistry.h>
// +++++++++++++++++++++++++

[...] // Debug #defines

// +++++++++++++++++++++++++
    // CppUnit: MFC TestRunner
    static AFX_EXTENSION_MODULE extTestRunner;
// +++++++++++++++++++++++++

[...] // Message-Maps and main construction of "theApp"

BOOL CMainApp::InitInstance()
{
#ifdef _AFXDLL
    Enable3dControls(); // Call this when using MFC in a shared DLL
#else
    Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

    // Declare & Run MFC TestRunner
    CppUnit::MfcUi::TestRunner runner;
    runner.addTest( CppUnit::TestFactoryRegistry::getRegistry().makeTest() );
    runner.run();

    /*
    CCppUnitGUIDemoDlg dlg;
    m_pMainWnd = &dlg;
    int nResponse = dlg.DoModal();
    if (nResponse == IDOK) {
    }
    else if (nResponse == IDCANCEL) {
    }
    */

    // Since the dialog has been closed, return FALSE so that we exit the
    // application, rather than start the application's message pump.
    return FALSE;
} // END of InitInstance()


You'll see that I commented out the invocation of the application's main dialog. This is not strictly necessary, but since I created this project just to be a test application I really didn't want it to display an additional window after the TestRunner.

9. Create Test Class(es)

Okay, now you're all setup and you should be able to build and run the project and the MFC TestRunner should come up. However, it won't have any tests to run, so it isn't very useful. ^_^

You can look at the CaesarCipherTests.h/.cpp files included in the project available to download above to for a workable example. But, basically you have to adhere to this rough template:

CSomeClassTest.h
// CSomeClassTest.h: interface of class CSomeClassTest.
//////////////////////////////////////////////////////////////////////

#ifndef _CSOMECLASSTEST_H_
#define _CSOMECLASSTEST_H_

#include <cppunit/extensions/HelperMacros.h>

// forward declare classes under test if necessary // e.g. class MyClass;
/// cppunit test class, main doc text
class CSomeClassTest : public CppUnit::TestFixture
{
    CPPUNIT_TEST_SUITE( CSomeClassTest );
    /// add tests to suite
        // e.g. CPPUNIT_TEST( test_1 );
        // e.g. CPPUNIT_TEST( test_2 );
    CPPUNIT_TEST_SUITE_END();

    /// define functions for fixture
    public:
        void setUp();
        void tearDown();

    /// declare protected variables for use by fixture
    protected:   
        // e.g. MyClass foo_;

    /// declare protected test methods
    protected:
        // e.g. void test_1();
        // e.g. void test_2();
};

#endif



CSomeClassTest.cpp

// CSomeClassTest.cpp: implementation of class CSomeClassTest.
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "CSomeClassTest.h"

// include headers for classes under tests
// e.g. #include "MyClass.h"

// include HelperMacros to use CppUnit macros
#include <cppunit/extensions/HelperMacros.h>

// register the test suite
CPPUNIT_TEST_SUITE_REGISTRATION( CSomeClassTest );

//////////////////////////////////////////////////////////////////////
// setup test fixture
//////////////////////////////////////////////////////////////////////

void CSomeClassTest::setUp() {
// put fixture creation code here // e.g. foo_ = new MyClass(); }

void CSomeClassTest::tearDown() {
// put fixture destruction code here // e.g. delete foo_; }

//////////////////////////////////////////////////////////////////////
// implement test methods
//////////////////////////////////////////////////////////////////////
/* e.g.
void CSomeClassTest::test_1() {
    MyClass dahoo(foo_);
    CPPUNIT_ASSERT_EQUAL(foo_,dahoo);
}
*/

/* e.g.
void CSomeClassTest::test_2() {
    MyClass dahoo;
    if (foo_ == dahoo) {
        CPPUNIT_FAIL("foo_ and dahoo should not be equal in this case!!");
    }
}
*/



And that should do it. You can include as many such test files in the project as you wish, and MFC TestRunner will give you the ability to selectively run suites or individual tests. Make sure you include the files for units under test (such as class MyObject in the above templates, and class CaesarCipher in the downloadable project) in the project.

Good luck!