CppUnitの導入について

C++による開発において、テストファーストを実践するに当たって、CppUnitの導入手順を記します。

最新のCppUnitは、こちらの CppUnit@SorceForge.netからダウンロードできます。また、ドキュメントは、こちらから参照できます。

この文章を記述しているとき(2003年6月時点)の最新版は、version 1.8.0で、開発中の snapshotは、version 1.9.10です。
ここでは、VC++.NET2003(以下、VC++と略す)にCppUnit (version 1.9.10)を導入する手順を示します。なお、VC++は、BETA版を利用しているため、設定の仕方、メニュー構成、画面のデザインが正式版と変わっている可能性があります。その点を考慮して参考にしてください。


ライブラリの作成

CppUnitは、独立したアプリケーションではなく、テストのターゲットとなるコードに、リンクさせて利用するライブラリです。
動作環境は、さまざまなOS (BeOS, Mac OS, Windows, OS Independent, Linux)に対応してあり、利用する環境に合わせてコンパイルし、ライブラリを構築する必要があります。

1.ダウンロードおよび展開

上記で示したサイトから利用するバージョンをダウンロードし、適当なディレクトリに解凍する。ここでは、展開したCppUnitのホームディレクトリを %CPPUNIT_HOME%と表記します。

2.VC++からCppUnitのワークスペース(%CPPUNIT_HOME%\src\CppUnitLibraries.dsw)を読み込み、必要なものをビルドする。

ビルドが完了すると、%CPPUNIT_HOME%\libにビルドされたファイルがコピーされます。DLLを利用すれば、実行ファイルのサイズは、小さくなります が、わたしは、静的ライブラリを利用しました。

cppunit/cppunit_dll 以外のライブラリはMFC環境下でテスト結果をグラフィカルに表示するときに利用します。 詳しくはCppUnitディストリビューションに同梱されているドキュメントをご覧ください。(VC++.NET2003では、コンパイルエラーが多発してビルドできませんでした)


テストコードの作成

テストコードの雛形は、河童プロジェクトなるものがあり、これを利用すると簡単に雛形が作れるようです。
私は、以下のように CppUnit付属のサンプルを参考に作成しました。

1.VC++の設定

CppUnitを利用するに当たって、インクルードファイル、ライブラリの参照パスを解決しなければなりません。今後VC++で利用し続けるのであれば、共通設定として定義してしまいましょう。「ツール」メニューの「オプション」でVC++ディレクトリの「インクルードファイル」、「ライブラリファイル」にて、%CPPUNIT_HOME%\include、%CPPUNIT_HOME\libをそれぞれに追加します。

そのターゲットのみであれば、コンパイルオプション(/I=%CPPUNIT_HOME%\include)などで参照パスの設定をします。

2.メインルーチンの作成

環境の設定が終わったら、ターゲットとなるプロジェクトを新規に作成します。そのソリューションにテスタ用プロジェクトを追加します。 テスタを追加したらテスタを「スタートアッププロジェクトに設定します」

テスタの命名は、XXXXtestや、testXXXXとつけることが、慣例のようです。プロジェクトのディレクトリ構成は、
%TARGET_HOME%
%TARGET_HOME%\testTarget
のように下位ディレクトリにつくると管理がしやすいです。(人それぞれかもしれません)

以下は、testTarget.cppの中身です。CppUnitのサンプルをそのまま流用しています。runner.addTest( suite );でCPPUNIT_TEST_SUITE()マクロで追加した テストメソッドが随時実行されるようになります。

testTarget.cpp
#include "stdafx.h"
#include <cppunit/CompilerOutputter.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>

int main(int argc, char* argv[])
{
	// Get the top level suite from the registry
	CPPUNIT_NS::Test *suite = CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest();

	// Adds the test to the list of test to run
	CPPUNIT_NS::TextUi::TestRunner runner;
	runner.addTest( suite );

	// Change the default outputter to a compiler error format outputter
	runner.setOutputter( new CPPUNIT_NS::CompilerOutputter( &runner.result(), std::cerr ) );

	// Run the test.
	bool wasSucessful = runner.run();

	// Return error code 1 if the one of test failed.
	int errorCode = wasSucessful ? 0 : 1;

	return errorCode;
}


3.テストコードの作成

VC++の機能(AppWizard)により、testTargetワークスペースに「一般C++クラス」を新規に追加します。このときは、ターゲットとなるクラスごとにテストコードを作成するため、ターゲットが、TableInfoクラスとした場合、 慣習的に TableInfoTestクラスとして作成するのがよいでしょう。

TableInfoTest.h
#pragma once
#include <cppunit/TestCase.h>
#include <cppunit/extensions/HelperMacros.h>
#include "../tableinfo.h"	// 必要なインクルードファイル	 [1]

/////////////////////////////////////////////////////////////////////////////
// テーブル情報管理クラスのテストクラス
class TableInfoTest : 	
	public CPPUNIT_NS::TestFixture
{
	CPPUNIT_TEST_SUITE( TableInfoTest );	// [2]
	CPPUNIT_TEST( testGetTableName );	// [3]
	CPPUNIT_TEST( testQueryTeble );	// [3]
	CPPUNIT_TEST_SUITE_END();		// [2]

public:
	TableInfoTest(void) {
	}

	~TableInfoTest(void) {
	}

	// 初期処理メソッド	[4]
	void setUp() {
	}

	// 終了処理メソッド[5]
	void tearDown() {
	}

	// テストメソッド
void testGetTableName(); // [6] };

TableInfoTest.cpp
#include <string>
#include <iostream>
#include "stdafx.h"
#include ".\tableinfotest.h"

// register test suite [7]
CPPUNIT_TEST_SUITE_REGISTRATION(TableInfoTest);
// テーブル名取得メソッドのテスト void TableInfoTest::testGetTableName() { try { // テストコード TableInfo tableInfo = new TableInfo(); CPPUNIT_ASSERT("master" == tableInfo->getTableName()); // [8] catch (...) { CPPUNIT_FAIL("TableInfoTest: テーブル名取得テスト NG!"); } std::cout << "TableInfoTest: テーブル名取得テスト OK !" << std::endl; // [9] }

(1) ターゲットのインクルードファイル
(2) CPPUNIT_TEST_SUITE(テストクラス名), CPPUNIT_TEST_SUITE_END()の間にテストメソッドを定義する
(3) CPPUNIT_TEST( xxx ) を利用し、テストメソッドを登録する
(4) 初期化処理を実装する(自動的に呼び出される)
(5) 後始末の処理を実装する(自動的に呼び出される)
(6) テストメソッドは、publicで定義する
(7) テストクラスを登録するマクロ
(8) CPPUNIT_ASSERTマクロでテスト結果を診断する
(9) CPPUNIT_FAILマクロでテストを中断する

8,9のマクロの他にテスト結果を検証するマクロは、CppUnitのドキュメントを参照してみてください。

また、.h/.cppを.cppファイルにまとめてしまってもOKです。

4.テスト実行

テストコード、メインルーチンが完成したら、ビルドし、ターゲットのコンパイルエラーを取り除き、実際の処理を実装します(テストファースト流に)。

ターゲットは、静的ライブラリとして構築し、テスタがターゲットのライブラリをリンクし、実行形式ファイルとしてテストします。デバッグモードでビルドすれば、インラインデバッグも可能です(スタートアッププロジェクトは、テスタに設定しておきます)。なお、cppunit.libをリンクすることを忘れずに。

また、テスタのプロパティ−コンパイルオプションにて、[C/C++]-[コード生成]-[ランタイムライブラリ]の設定を、
  マルチスレッド デバッグ DLL (/MDd)
にしておく必要があります。

.TableInfoTest: テーブル名取得テスト OK !

OK (1)
Press any key to continue




 

あとは、クラスを追加していくごとに、(3)のようにテストコードを追加していけば、テストの自動化ができます。テスト・スイートの順番は、ABC順のようです。

JBuilderに組み込まれたJUnitのように、メソッド単位のテスト実行は、できないようです。
(できるやり方を知っている人がいたら、ぜひ教えてください!)


以上