株式会社エス・スリー・フォー

CppUnit 導入ガイド

ダウンロードとインストール

CppUnitは http://sourceforoge/projects/cppunit/ からダウンロードできます。2002.11時点での最新版は1.8.0(βは1.9.10)です。

zipもしくはtar+gzipで圧縮されているので、適当なディレクトリに展開してください。

以下の説明はWindows/Visual C++ v6.0 環境で、CppUnitのルート・ディレクトリを<CPPUNIT>と表記します。

ライブラリのビルド

CppUnitはツール/ユーティリティの類ではありません。テスト対象およびテストコードと一緒にリンクして実行モジュールを生成する’ライブラリ’です。

CppUnitは様々なOS/処理系に対応しており、その使用に先だってライブラリをビルドしなければなりません。

Visual Studio IDE から プロジェクト:<CPPUNIT>\src\CppUnitLibraries.dsw をオープンし、ビルド/バッチビルド-ダイアログからcppunitおよびcppunit_dllをチェックしてビルドします。

これによって、<CPPUNIT>\libに必要なライブラリ(とDLL)が生成されます。DLLはパスの通ったディレクトリにコピーしておくとよいでしょう。DLL版CppUnitはテストの実行モジュール・サイズを小さく抑えることができます。

batchbuild

cppunit/cppunit_dll以外のライブラリはMFC環境下でテスト結果をグラフィカルに表示するときに利用します。詳しくはCppUnitディストリビューションに同梱されているドキュメントをご覧ください。

テストコードの雛型

テストコードの雛型を示します。この雛型は様々なテストに応じて書き換えて利用してください。

XXXTest.cpp

/*
 * test skeleton XXXTest.cpp [1]
 */

// 必要なヘッダの #include [2]

// ex. #include "XXX.h"

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestAssert.h>

// XXX をテストする [3]
class XXXTest : public CppUnit::TestFixture {

  CPPUNIT_TEST_SUITE(XXXTest); // [4]
  CPPUNIT_TEST(test_one);      // [5]

  CPPUNIT_TEST(test_two);      // [5]
  CPPUNIT_TEST(test_three);    // [5]

  CPPUNIT_TEST(test_four);     // [5]
  CPPUNIT_TEST(test_five);     // [5]

  CPPUNIT_TEST(test_six);      // [5]
  CPPUNIT_TEST_SUITE_END();    // [4]

private:

  // 必要な メンバ変数/関数 [6]

public:

  /*
   * コンストラクタ/デストラクタ [7]

   */
  XXXTest() {
  }

  ~XXXTest() {

  }

  /*
   * 初期化/後始末 [8]
   */
  virtual void setUp() {
  }

  virtual void tearDown() {
  }

  /*
   * テスト・ケース [9]

   */
  void test_one() {
    CPPUNIT_ASSERT( 0 == 1 );

  }

  void test_two() {
    CPPUNIT_ASSERT_MESSAGE( "0 is not equal to 1", 0 == 1 );

  }

  void test_three() {
    CPPUNIT_FAIL( "never reached here!" );

  }

  void test_four() {
    CPPUNIT_ASSERT_EQUAL( 0, 1 );

  }

  void test_five() {
    CPPUNIT_ASSERT_EQUAL_MESSAGE( "1 is not equal to 0", 0, 1 );

  }

  void test_six() {
    CPPUNIT_ASSERT_DOUBLES_EQUAL( 1.23, 1.24, 0.001 );

  }

};

/*
 * register test suite
 */
CPPUNIT_TEST_SUITE_REGISTRATION(XXXTest); // [10]
[1] ファイル名
慣習的に、テスト対象であるモジュール名の末尾に’Test’を付加します。たとえば、’Counter’のテストコードは’CounterTest.cpp’のように。
[2] インクルード
必要なインクルードをここに記述します。
[3] テストクラス
テストクラスはテスト対象に対するテスト・ケースの集合体で、
ファイル名と同じクラス名とするのが望ましいでしょう。
[4] テスト・スイート
CPPUNIT_TEST_SUITE(テストクラス名);CPPUNIT_TEST_SUITE_END(); の間にテスト・ケースを記述します。
[5] テスト・ケース(メソッド)の登録
CPPUNIT_TEST(テスト・ケース名); によって、テスト・ケースを登録します。
[6] メンバ変数/関数
各テスト・ケースの実行に必要なメンバ変数/関数を宣言します。
[7] コンストラクタ/デストラクタ
必要に応じ、コンストラクタ/デストラクタを定義します。なお、テストクラスのインスタンスは各テスト・ケース毎に生成されます。
[8] 初期化/後始末
setUp(初期化)とtearDown(後始末)は、それぞれ各テスト・ケース実行の直前/直後に呼び出されます。
[9] テスト・ケース(メソッド)
テスト・ケースは引数/戻り値を持たないメソッドで、テスト項目毎に用意します。
[10] テスト・スイート登録
CPPUNIT_TEST_SUITE_REGISTRATION(テストクラス名); によって、テスト・スイートが登録されます。

アサート・マクロ

各テスト・ケースの中では、テスト対象が正常に機能しているかを検証するコードを記述します。

以下のアサート・マクロが用意されています。

各アサート・マクロはそれぞれ特定の条件を満たさないとき、テスト失敗(failre)とみなしてそれ以降の処理をせずにテスト・ケースから脱出し、次のテスト・ケースに移ります。

CPPUNIT_ASSERT( condition );
conditionが偽(false,0)であったとき、失敗します。
CPPUNIT_ASSERT_MESSAGE( message, condition );
conditionが偽であったとき、失敗します。このときmessageを出力します。
CPPUNIT_FAIL( message );
必ず失敗します。messageを出力します。
CPPUNIT_ASSERT_EQUAL( expected, actual );
得られた結果actualが期待する値expectedでなかったとき、すなわちexpected != actualのときに失敗します。
CPPUNIT_ASSERT_EQUAL_MESSAGE( message, expected, actual );
得られた結果actualが期待する値expectedでなかったとき、すなわちexpected != actualのときに失敗します。このときmessageを出力します。
CPPUNIT_ASSERT_DOUBLES_EQUAL( expected, actual, delta );
得られた結果actualと期待する値expectedとの差がdeltaより大きいとき、失敗します。

メイン・ルーチン

以上のようにして作られたいくつかのテスト・スイートを実行し、その結果を出力するメイン・ルーチンの雛形を示します。

出力フォーマットに応じ、3種の雛形を用意しました。それぞれの出力結果をコードと併せて示します。

テキスト形式

#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/TextOutputter.h>

#include <cppunit/extensions/TestFactoryRegistry.h>

int main() {
  CppUnit::TextUi::TestRunner runner;

  runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());

  CppUnit::Outputter* outputter =
    new CppUnit::TextOutputter(&runner.result(),std::cout);

  runner.setOutputter(outputter);
  return runner.run() ? 0 : 1;

}
!!!FAILURES!!!
Test Results:
Run:  6   Failures: 6   Errors: 0


1) test: XXXTest::test_one (F) line: 49 XXXTest.cpp
assertion failed
- Expression: 0 == 1


2) test: XXXTest::test_two (F) line: 53 XXXTest.cpp
assertion failed
- 0 is not equal to 1


3) test: XXXTest::test_three (F) line: 57 XXXTest.cpp
forced failure
- never reached here!


4) test: XXXTest::test_four (F) line: 61 XXXTest.cpp
equality assertion failed
- Expected: 0
- Actual  : 1


5) test: XXXTest::test_five (F) line: 65 XXXTest.cpp
equality assertion failed
- Expected: 0
- Actual  : 1
- 1 is not equal to 0


6) test: XXXTest::test_six (F) line: 69 XXXTest.cpp
equality assertion failed
- Expected: 1.23
- Actual  : 1.24

コンパイラ出力形式

#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/extensions/TestFactoryRegistry.h>

int main() {

  CppUnit::TextUi::TestRunner runner;
  runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());

  CppUnit::Outputter* outputter =
    new CppUnit::CompilerOutputter(&runner.result(),std::cout);

  runner.setOutputter(outputter);
  return runner.run() ? 0 : 1;

}
XXXTest.cpp(44):Assertion
Test name: XXXTest::test_one
assertion failed
- Expression: 0 == 1

XXXTest.cpp(48):Assertion
Test name: XXXTest::test_two
assertion failed
- 0 is not equal to 1

XXXTest.cpp(52):Assertion
Test name: XXXTest::test_three
forced failure
- never reached here!

XXXTest.cpp(56):Assertion
Test name: XXXTest::test_four
equality assertion failed
- Expected: 0
- Actual  : 1

XXXTest.cpp(60):Assertion
Test name: XXXTest::test_five
equality assertion failed
- Expected: 0
- Actual  : 1
- 1 is not equal to 0

XXXTest.cpp(64):Assertion
Test name: XXXTest::test_six
equality assertion failed
- Expected: 1.23
- Actual  : 1.24

Failures !!!
Run: 6   Failure total: 6   Failures: 6   Errors: 0

XML形式

#include <cppunit/ui/text/TestRunner.h>

#include <cppunit/XmlOutputter.h>
#include <cppunit/extensions/TestFactoryRegistry.h>

int main() {

  CppUnit::TextUi::TestRunner runner;
  runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());

  CppUnit::Outputter* outputter =
    new CppUnit::XmlOutputter(&runner.result(),std::cout);

  runner.setOutputter(outputter);
  return runner.run() ? 0 : 1;

}
<?xml version="1.0" encoding='ISO-8859-1' standalone='yes' ?>
<TestRun>
  <FailedTests>
    <FailedTest id="1">

      <Name>XXXTest::test_one</Name>
      <FailureType>Assertion</FailureType>
      <Location>
        <File>XXXTest.cpp</File>

        <Line>49</Line>
      </Location>
      <Message>assertion failed
- Expression: 0 == 1
</Message>
    </FailedTest>
    <FailedTest id="2">

      <Name>XXXTest::test_two</Name>
      <FailureType>Assertion</FailureType>
      <Location>
        <File>XXXTest.cpp</File>

        <Line>53</Line>
      </Location>
      <Message>assertion failed
- 0 is not equal to 1
</Message>
    </FailedTest>
    <FailedTest id="3">

      <Name>XXXTest::test_three</Name>
      <FailureType>Assertion</FailureType>
      <Location>
        <File>XXXTest.cpp</File>

        <Line>57</Line>
      </Location>
      <Message>forced failure
- never reached here!
</Message>
    </FailedTest>
    <FailedTest id="4">

      <Name>XXXTest::test_four</Name>
      <FailureType>Assertion</FailureType>
      <Location>
        <File>XXXTest.cpp</File>

        <Line>61</Line>
      </Location>
      <Message>equality assertion failed
- Expected: 0
- Actual  : 1
</Message>
    </FailedTest>
    <FailedTest id="5">

      <Name>XXXTest::test_five</Name>
      <FailureType>Assertion</FailureType>
      <Location>
        <File>XXXTest.cpp</File>

        <Line>65</Line>
      </Location>
      <Message>equality assertion failed
- Expected: 0
- Actual  : 1
- 1 is not equal to 0
</Message>
    </FailedTest>
    <FailedTest id="6">

      <Name>XXXTest::test_six</Name>
      <FailureType>Assertion</FailureType>
      <Location>
        <File>XXXTest.cpp</File>

        <Line>69</Line>
      </Location>
      <Message>equality assertion failed
- Expected: 1.23
- Actual  : 1.24
</Message>
    </FailedTest>
  </FailedTests>

  <SuccessfulTests></SuccessfulTests>
  <Statistics>
    <Tests>6</Tests>
    <FailuresTotal>6</FailuresTotal>

    <Errors>0</Errors>
    <Failures>6</Failures>
  </Statistics>
</TestRun>