お気に入りツール紹介:C++test (3)

今回は、C++testをより便利に使うためのTipsをご紹介します。

なお、これらの情報は2010年1月25日の時点で最新の、C++test Ver7.2向けのものです。

CPPTEST_ マクロのオートコンプリート

ユニットテストを記述する際、CPPTEST_POST_CONDITION_INTEGER などと記述します。しかし、これがメンドクサイ!

まだ小文字なら我慢できますが、大文字でこの長さっていうのはいただけません。CapsLockかけるのも面倒だし、Shiftおしながら入力するのも大変だし・・・

そこで、VCのインクルードパスに、つぎの値を追加しましょう。(インストール位置、C++testのバージョンなどに応じて、適宜修正してください)

C:\Program Files\Parasoft\C++test7.2VS2005Extension\plugins\com.parasoft.xtest.libs.vstudio.cpp\os\win32\x86\etc\include

ひょっとすると、このあとでIntelliSenseのデータベースファイル(*.ncb)を削除したほうがいいかもわかりません。

そうすると、こんな事ができます。

  1. CPPTEST_P まで入力
  2. Ctrl + Space
  3. 選ぶ

オートコンプリート

これでもう、あの長ったらしいキーワードを全部入力しなくてもいい!

Qtのユニットテストがビルドできない場合 1

Qtプロジェクトをテストしようとする時、たまに「シンボルが見つからない!」という旨のエラーがでる場合があります。(私は、Qtライブラリをスタティックリンクライブラリとして使用しています。これがいけないのかも分かりません。)

明らかにQtクラスのシンボルなので、勝手に付け足すわけにもいかず・・・この手のエラーは、設定変更で回避できる場合があります。

シンボル識別モードを「オフ」にする

C++testの「テストコンフィグレーション」にて、「実行」>「シンボル」の、ライブラリシンボル識別モードを「オフ」にしちゃいましょう。これでエラーは出なくなり、ちゃんとテスト実行も出来ます。

Qtのユニットテストがビルドできない場合 2

QtのVisual Studio AddIn 1.10か、それより以前の物を使っている場合、特に注意が必要です。

Qtでは、mocで生成されたファイルが、GeneratedFiles フィルタの下に配置されます。この時、Debug用とRelease用とで2種類のファイルが、同じGeneratedFilesフィルタの下に配置されてしまいます

どうやらC++testでは、「うん?なんで同じフィルタの下に、おなじファイル名のものが2つもあるんじゃぁぁあああ!」とお怒りになられるようで、ユニットテストのビルドに失敗してしまいます。

そういう場合には、GeneratedFilesフィルタの下にさらに Debug, Release という2つのフィルタを用意し、それぞれにDebug用のファイル、Release用のファイルを配置するようにしてください。

DebugとReleaseのフィルタに分ける

ちなみに、 AddIn 1.12以降では自動的にそうなるようになっていますので、心配は無用です。

テストケースのステップ実行方法

テストケースの実行は出来ても、なぜかステップ実行が出来ない・・・という事で、1時間くらい悩んだ記憶があります。

テストケースの実行は「ソリューションエクスプローラ」からも出来ますが、ステップ実行するには「クラスビュー」から実行する必要があります。

まず、クラスビューにて、ステップ実行したいテストケースを選択します。

クラスビューによる選択

ここから右クリックで、「Debug Unit Tests」を選択します。

Debug Unit Tests

すると、このテストケースの頭に自動的にブレークが入り、ステップ実行できるようになります。

テストケースの頭でブレーク

いくらブレークポイントを手で設定しておいても、ソリューションエクスプローラから「Debug Unit Tests」してしまうとデバッグが行われません。注意が必要です。

new演算子をスタブ化

new演算子をスタブ化するには、次のコードを使用します。

void* CppTest_Stub_operator_new( size_t size )
{
	void* result = NULL;
	std::string testCaseName = CppTest_GetCurrentTestCaseName();
	if( testCaseName.find( "_Fail_new" ) != std::string::npos )
	{
		// 失敗時の処理
		throw std::bad_alloc();
	}
	else
	{
		// 成功時の処理
		result = malloc( size );
	}
	return( result );
}

このスタブを使用すると、テストケース名に _Fail_new が含まれている場合のみ、newが失敗するようになります。

いくつか注意点があります。

まず、new失敗時に、std::bad_alloc()を投げています。これは、非MFCアプリの場合の挙動です。MFCアプリ内にてnewを失敗させた時には、CMemoryException()を投げてください。

次に、成功時の処理として malloc を使用していますが、これは正しいです。このようにしても、ちゃんとメモリが確保された上に、コンストラクタが呼び出されるという事を確認しています。一方、ここに new char[ size ] などと書いてしまうと、メモリ破壊がおきます。ですから、ここは必ず malloc を使用するようにします。

Privateメソッドのテスト

ユニットテストをめぐる議論の1つに、「Privateメソッドをどうしようね?」という物が有ります。

Privateメソッドをテストするメリットは、「より深くテストが出来る」というものです。

Privateメソッドをテストするデメリットは、「本来、そのクラスの果たすべき責務のみをテストするべき。そうしないと、リファクタリング作業が煩雑になる」というものです。

この問題に関する私の答えは、「両方つくったらいいんじゃない?」です。つまり、Publicメソッドのみをテストするもの。ProtectedやPrivateメソッドまでテストするもの。2つ作ったらいいんじゃないかと思います。

C++testでは、各テストスイートを、フィルタを使用して階層構造に分けることができます。

handcodeと、handcode_coverageフィルタ

クラスの果たすべき責務をテストするのは、「handcode」フィルタのしたに、HandTestSuite_Function_ というプレフィクスをつけたテストスイートにて実施します。

もっと内部についてもテストするには、「handcode_coverage」フィルタのしたに、HandTestSuite_Coverate_ というプレフィクスをつけたテストスイートにて実施します。

C++testには、「このフィルタの下にあるテストスイートだけを、ぜーんぶ実行!」という事ができます。(フィルタを右クリックして、テスト実行するだけ)

ですから、リファクタリング時には基本的に「handocode」フィルタの下にあるものしかテストしません。そうやってリファクタリングが落ち着いた後で、テストの手が届いていないところがあれば、その時に「handcode_coverage」の下のテストケースでもって詳細なテストを行います。

このように、C++testではテストスイートを柔軟に管理できますので、Privateメソッドのテストの問題に対しても、柔軟に対処する事ができます。

以上です

もっと細々とした話もありますが、だいたいこれでC++testについて書いておきたい事はすべてです。

ちなみに販売元に断って書いているわけではないので、ひょっとして「なんで勝手にかくんじゃー!」と怒られてしまうかも分かりません。ある日とつぜんこの記事が消えたら、あぁそういうことかー、と思ってくださいな。

逆に「もっと書けー」とか言われたら、またなにか続きを企画するかも分かりません。