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のように、メソッド単位のテスト実行は、できないようです。
(できるやり方を知っている人がいたら、ぜひ教えてください!)