Version: 1.0
Date: 2004/08/05
Mobile Internet APL チーム
CppUnit を使用した
--- 使用上の注意 以下は当文書を利用するにあたっての注意事項です。以下について了承されない方は、速やかに当文書を破棄 して下さい。 ! 当文書中のすべての情報について その正確性・有効性は十分配慮していますが、不正確な内容がある可能性があります。それら情報の使 用の際に生じた損害等については一切の責任は負わないことをご了承下さい。 以上
Copyright © 2003-2004 Fujisystems Inc., All Rights Reserved.
Change History
Date New Ver
Cate-
gory Section Changes Changed by
2004/08/05 1.0 A - 新規作成 上野
目次
1 はじめに... 1 1.1 参考文献... 1 1.2 略語一覧... 1 2 基本方針... 2 3 CppUnit の環境構築... 3 4 Visual C++ 6.0 の設定... 4 5 実プロジェクトの作成... 6 6 テストプロジェクトの作成... 9 7 テストクラスの作成... 13 8 テストプロジェクトに実プロジェクトを追加 ... 16 9 テストコードの記述... 17 10 実クラスの作成... 19 11 TIPS ... 21 11.1 リソースの競合... 21 11.2 MFC のソケットクラスの使用... 231
1 はじめに
本ドキュメントは、CppUnit を使用した「テストファースト」なプロジェクトを、Microsoft Visual C++ 6.0 (SP5)にて 実現させる場合の導入指南書となることを目的としています。 実プロジェクトの導入の参考になれば幸いです。
1.1 参考文献
[1] 大月美佳著 ”CPPUNIT による実践テスト技法” 秀和システム 2003/07/201.2 略語一覧
略語 説明 VC Microsoft Visual C++ 6.0 MFC Microsoft Foundation ClassXP eXtreme Programming
URL Uniform Resource Locator SDI Single Document Interface MDI Multiple Document Interface RTTI Run-Time Type Information
2
2 基本方針
● 実プロジェクトとテストプロジェクトを分離する テストコードは非常に有用なものですが、実際のプログラムの動作には必要ありません。このため、実プロジ ェクトのみ納品できるようにプロジェクトを 2 つに分離します。 ● UI のテストは自動化しない UI(ユーザインターフェース)のテストは CppUnit による自動化テストには不向きですので、テスト対象外としま す。(例えば XP の場合なら、ユーザによる受入テストで実施すべきものです) つまり、テストプロジェクトはコンソールアプリケーションで十分、ということになります。 ● MFC の使用について MFC の使用、不使用に関係なく CppUnit を導入することが可能です。 以上を踏まえ、本ドキュメントでは以下のような開発環境を構築することとします。 C: ¥CppUnit CppUnit インストールフォルダ ¥Project VC プロジェクトルートフォルダ ¥Real 実プロジェクトフォルダ (MFC AppWizard(exe)で作成) ¥Test テストプロジェクトフォルダ (Win32 Console Application で作成)また、実クラスとして“Dummy”、テストクラスとして“testDummy”クラスを作成します。
Dummy は引数で与えられた自然数を素数であるかどうか判断する“isPrimeNumber”関数を持ちます。
3
3 CppUnit の環境構築
CppUnit を以下の URL からダウンロードします。 https://sourceforge.net/project/showfiles.php?group_id=11795 (2004/08/05 現在、最新版はバージョン 1.10.2、ファイル名は cppunit-1.10.2.tar.gz) ダウンロードしたファイルを解凍し、上記フォルダ(C:¥CppUnit)にコピーします。 examples フォルダにある VC 用のプロジェクトファイル(C:¥CppUnit¥examples¥examples.dsw)を開きます。 図 3-1 examples プロジェクト メニューの「ビルド」「バッチビルド」を選択し、すべてのチェックが入っているのを確認してから「リビルド」を実 行します。 図 3-2 バッチビルド4
4 Visual C++ 6.0 の設定
VC を起動し、メニューの「ツール」「オプション」を選択します。 「 デ ィ レ ク ト リ 」 タ ブ か ら 表 示 す る デ ィ レ ク ト リ の 「 イ ン ク ル ー ド フ ァ イ ル 」 を 選 択 し 、 デ ィ レ ク ト リ に “C:¥CppUnit¥include”を追加します。 図 4-1 参照するインクルードディレクトリ 同じく、表示するディレクトリの「ライブラリファイル」を選択し、ディレクトリに“C:¥CppUnit¥lib”を追加します。 図 4-2 参照するライブラリディレクトリ5 同じく、表示するディレクトリの「ソースファイル」を選択し、ディレクトリに“C:¥CppUnit¥src¥cppunit”を追加しま す。 図 4-3 参照するソースディレクトリ 「OK」ボタンを押下します。
6
5 実プロジェクトの作成
メニューの「新規作成」を選択します。 「プロジェクト」タブから「MFC AppWizard(exe)」を選択し、プロジェクト名に“Real”、位置に“C:¥Project¥Real”を 入力してから「OK」ボタンを押下します。 図 5-1 実プロジェクト作成 必要な情報を Wizard の指示に従って入力し、プロジェクトを作成します。 なお、アプリケーションの種類(SDI/MDI/ダイアログベース)や各種設定に、CppUnit は全く依存しません。7 メニューの「プロジェクト」「設定」を選択します。 設定の対象で「すべての構成」を選択します。 「C/C++」タブから「C++ 言語」カテゴリを選択し、「ランタイムタイプ情報(RTTI)を有効にする」にチェックを入 れます。 図 5-2 ランタイムタイプ情報を有効にする
8 設定の対象で「Win32 Debug」を選択します。 「C/C++」タブから「コード生成」カテゴリを選択し、使用するランタイムライブラリが「マルチスレッド(DLL、デバ ッグ)」になっていることを確認します。 図 5-3 マルチスレッドであることを確認 設定の対象で「Win32 Release」を選択します。 「C/C++」タブから「コード生成」カテゴリを選択し、使用するランタイムライブラリが「マルチスレッド(DLL)」にな っていることを確認します。 「OK」ボタンを押下し、ここでビルドします。 成功することを確認したら、一度プロジェクトファイルを閉じます。
9
6 テストプロジェクトの作成
メニューの「新規作成」を選択します。
「 プ ロ ジ ェ ク ト 」 タ ブ か ら 「 Win32 Console Application 」 を 選 択 し 、 プ ロ ジ ェ ク ト 名 に “ Test ” 、 位 置 に “C:¥Project¥Test”を入力してから「OK」ボタンを押下します。
図 6-1 テストプロジェクト作成
アプリケーションの種類で「MFC をサポートするアプリケーション」を選択し、「終了」ボタンを押下します。
10 メニューの「プロジェクト」「設定」を選択します。 設定の対象で「すべての構成」を選択します。 「C/C++」タブから「C++ 言語」カテゴリを選択し、「ランタイムタイプ情報(RTTI)を有効にする」にチェックを入 れます。 「C/C++」タブから「プリプロセッサ」カテゴリを選択し、インクルードファイルへのパスに“..¥Real”を入力します。 図 6-3 インクルードパスの追加
11
「 ビ ル ド 後 の 処 理 」 タ ブ か ら ビ ル ド 後 の 処 理 の 説 明 に “ Test Start !! ” 、 ビ ル ド 後 の 処 理 コ マ ン ド に “$(TargetPath)”を入力します。
12 設定の対象で「Win32 Debug」を選択します。 「C/C++」タブから「コード生成」カテゴリを選択し、使用するランタイムライブラリが「マルチスレッド(DLL、デバ ッグ)」になっていることを確認します。 「リンク」タブから「一般」カテゴリを選択し、オブジェクト/ライブラリモジュールに“cppunitd.lib”を追加します。 (“d”付きはデバッグ環境用) 図 6-5 CppUnit のスタティックライブラリを追加 設定の対象で「Win32 Release」を選択します。 「C/C++」タブから「コード生成」カテゴリを選択し、使用するランタイムライブラリが「マルチスレッド(DLL)」にな っていることを確認します。 「リンク」タブから「一般」カテゴリを選択し、オブジェクト/ライブラリモジュールに“cppunit.lib”を追加します。 (“d”なしはリリース環境用) 「OK」ボタンを押下します。
13
7 テストクラスの作成
ワークスペースウィンドウの“Test クラス”を右クリックし、サブメニューの「クラスの新規作成」を選択します。 図 7-1 クラスの新規作成 クラスの種類に「Generic クラス」、クラス名に“testDummy”、基本クラスの派生元に“CppUnit::TestFixture”と 入力し、「OK」ボタンを押下します。 図 7-2 testDummy クラスの新規作成14
テストクラスのヘッダファイル(testDummy.h)を以下のように編集します。
#include <cppunit/extensions/HelperMacros.h>
class testDummy : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(testDummy); //自クラス名を引数に入力 CPPUNIT_TEST(test); //テスト関数名を引数に入力(複数可) CPPUNIT_TEST_SUITE_END(); public: testDummy(); virtual ~testDummy(); void setUp () { //実クラスのインスタンス作成 } void tearDown () { //実クラスのインスタンス削除 } void test(); //テスト関数 }; テストクラスのソースファイル(testDummy.cpp)を以下のように編集します。 //テスト関数 void testDummy::test() { CPPUNIT_ASSERT(FALSE); //わざと失敗させる } メイン(_tmain)関数のあるソースファイル(Test.cpp)を以下のように編集します。 #include <cppunit/ui/Text/TestRunner.h> #include <cppunit/CompilerOutputter.h> #include "testDummy.h" //テストクラス int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) {
int nRetCode = 0;
// MFC の初期化および初期化失敗時のエラーの出力
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) {
// TODO: 必要に応じてエラー コードを変更してください。 cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1;
} else {
15 //ユニットテストを実行する CppUnit::TextUi::TestRunner runner; runner.addTest(testDummy::suite()); //テストクラス(複数可) CppUnit::Outputter* outputter = CppUnit::CompilerOutputter::defaultOutputter(&runner.result(), std::cout); runner.setOutputter(outputter); nRetCode = !(runner.run()); } return nRetCode; } ここでビルドします。ビルドをするとユニットテストを自動的に実行します。 アウトプットウィンドウに以下のように出力されていれば、つまりユニットテストが実行され、かつテストに失敗 することが確認できれば、ここまでは成功です。 コードを生成中... リンク中... Test Start !! .F C:¥Project¥Test¥testDummy.cpp(32):Assertion Test name: testDummy::test
assertion failed - Expression: FALSE Failures !!!
Run: 1 Failure total: 1 Failures: 1 Errors: 0 c:¥winnt¥system32¥cmd.exe の実行エラー
16
8 テストプロジェクトに実プロジェクトを追加
メニューの「プロジェクト」「プロジェクトをワークスペースへ挿入」を選択します。 実プロジェクト(C:¥Project¥Real¥Real.dsp)を選択し、「依存関係」にチェックを入れてから「OK」ボタンを押下し ます。 図 8-1 テストプロジェクトに実プロジェクトを追加17
9 テストコードの記述
アクティブプロジェクトを「Test」に変更します。 テストクラスのヘッダファイル(testDummy.h)を以下のように編集します。 #include "Dummy.h"class testDummy : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(testDummy); //自クラス名を引数に入力 CPPUNIT_TEST(test); //テスト関数名を引数に入力(複数可) CPPUNIT_TEST(testisPrimeNumber); CPPUNIT_TEST_SUITE_END(); public: testDummy(); virtual ~testDummy(); void setUp () { //実クラスのインスタンス作成
target = new Dummy; } void tearDown () { //実クラスのインスタンス削除 delete target; } void test(); //テスト関数 void testisPrimeNumber(); //素数判定 テスト関数 private: Dummy *target; //実クラスへのポインタ }; テストクラスのソースファイル(testDummy.cpp)を以下のように編集します。 //テスト関数 void testDummy::test() { CPPUNIT_ASSERT(TRUE); //必ず成功させる } //素数判定 テスト関数 void testDummy::testisPrimeNumber() { BOOL bRet; bRet = target->isPrimeNumber (0); //0 は素数ではない CPPUNIT_ASSERT(!bRet);
18 bRet = target->isPrimeNumber (1); //1 は素数ではない CPPUNIT_ASSERT(!bRet); bRet = target->isPrimeNumber (2); //2 は素数 CPPUNIT_ASSERT(bRet); bRet = target->isPrimeNumber (4); //4 は素数ではない CPPUNIT_ASSERT(!bRet); … } ここでビルドし、リンクエラーになることを確認します。
19
10 実クラスの作成
ワークスペースウィンドウの“Real クラス”を右クリックし、サブメニューの「クラスの新規作成」を選択します。
クラスの種類に「Generic クラス」、クラス名に“Dummy” と入力し、「OK」ボタンを押下します。 実クラスのヘッダファイル(Dummy.h)を以下のように編集します。 class Dummy { public: Dummy(); virtual ~Dummy();
BOOL isPrimeNumber(int nNumber); };
実クラスのソースファイル(Dummy.cpp)を以下のように編集します。
//素数判定
BOOL Dummy:: isPrimeNumber (int nNumber) { if(nNumber <= 0) { return FALSE; //0 は素数でない } if(nNumber == 1) { return FALSE; //1 は素数でない }
int nLoop = nNumber / 2; for(int i = 2; i <= nLoop; i++) {
if(nNumber % i == 0) { return FALSE; //割り切れれば素数ではない } } return TRUE; //割り切れなければ素数 }
20 ここで最大の注意点ですが、「Test」プロジェクトの方にも実クラスのソースファイル(Dummy.cpp)を追加する 必要があります。 一番簡単な方法はソースファイルの編集ウィンドウで右クリックをし、サブメニューの「プロジェクトへファイルの 挿入」「Test」を選択することです。 図 10-1 実クラスのソースファイルをテストプロジェクトに追加 それではビルドします。 アウトプットウィンドウに以下のように出力されていれば、すべて成功です。 コードを生成中... リンク中... Test Start !! . OK (2) Test.exe - エラー 0、警告 0
21
11 TIPS
ここでは、実際のプロジェクトで起こったトラブルと、その回避方法を紹介します。11.1 リソースの競合
ユニットテストではテストプロジェクトから実プロジェクト内のクラスを呼び出すことになりますが、例えば実プロ ジェクトで CString::LoadString()などのリソースを参照しているコードがあった場合、テストプロジェクト側のリソ ースを参照しに行ってしまい、エラーが発生する可能性があります。 この問題を回避するために、リソースファイルおよび Resource.h をプロジェクト間で共有する方法を示します。 アクティブプロジェクトをテストプロジェクトに変更します。ワークスペースウィンドウの File View から、テストプロジェクトのリソースファイル(Test.rc)および Resource.h を削除します。Resource View でリソースが削除されているのを確認します。
22 メニューの「プロジェクト」「プロジェクトへ追加」「ファイル」を選択し、実プロジェクトのリソースファイル(Real.rc) を追加します。 図 11-2 実プロジェクトのリソースをテストプロジェクトに追加 File View で確認します。 図 11-3 実プロジェクトのリソースがテストプロジェクトに追加されている ただし、こうするとテストプロジェクト用のリソースが扱えなくなります。 テストプロジェクトを MFC AppWizard など UI 付きで作成する場合は、面倒でも対象リソースを二重化する必要 があります。
23
11.2 MFC のソケットクラスの使用
CAsyncSocket など MFC のソケットクラスを実プロジェクトで使用している場合、ユニットテストでエラーが発生 する可能性があります。これはソケットの初期化関数をテストプロジェクトで呼び出していない場合に発生しま す。 この問題はメイン関数にてユニットテストを実行する前に、以下のようなコードを追加することで解決します。 #include <afxsock.h>int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) {
int nRetCode = 0;
// MFC の初期化および初期化失敗時のエラーの出力
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) {
// TODO: 必要に応じてエラー コードを変更してください。 cerr << _T("Fatal Error: MFC initialization failed") << endl; nRetCode = 1; } else { //MFC のソケットを初期化する if(!AfxSocketInit()) {
cerr << _T("Fatal Error: AfxSocketInit") << endl; return 1; } //ユニットテストを実行する CppUnit::TextUi::TestRunner runner; runner.addTest(testDummy::suite()); //テストクラス(複数可) CppUnit::Outputter* outputter = CppUnit::CompilerOutputter::defaultOutputter(&runner.result(), std::cout); runner.setOutputter(outputter); nRetCode = !(runner.run()); } return nRetCode; }