2012年2月25日土曜日

Google Test を使ってみる - その4(オプション)

Google Test では様々なオプションを環境変数または起動時引数に指定できます。
今回はそちらの紹介をしたいと思います。

オプション内容
--gtest_list_testsテストの一覧を出力します。
--gtest_filter=<条件>実行するテストを限定します。
--gtest_also_run_disabled_testsDISABLE なテストも実行します。
--gtest_repeat=<COUNT>テストの繰り返し回数を指定します。
負数にすると無限に繰り返します。
--gtest_shuffleテストの実行順序をシャッフルします。
--gtest_random_seed=<NUMBER>シャッフルする際の乱数シードを指定します。
0 を指定すると時間からシードを決定します。
--gtest_color=<yes|no|auto>出力を色付で表示するか選択します。
--gtest_print_time=0テストの実行時間を出力しないようにします。
--gtest_output=xml[:DIRECTORY|:FILE]指定のディレクトリまたはファイルにテスト結果の xml を出力します。デフォルトのファイル名は test_details.xml です。
--gtest_break_on_failureテストが失敗したときに break します。
--gtest_throw_on_failureテストが失敗したときに例外をスローします。
--gtest_catch_exceptionsテストが例外をキャッチするようにします。
--helpヘルプを表示します。

環境変数の場合は、最初の -- をのぞいた変数名を使います。(GTEST_FILTER, GTEST_COLOR, etc...)

よく使うオプション
それぞれのオプションの機能については、マニュアルを見ればわかるのでそちらを見ていただいて、ここでは個人的に使用頻度の高い機能と思うオプションを紹介します。

--gtest_filter=<条件>
実行するテストを指定します。
条件には、 * が使用できます。 * は任意の文字列にマッチします。
--gtest_filter=* ですべてのテスト名にマッチします。
--gtest_filter=FooTest.* でテストケース FooTest 内のすべてを実行します。

:(コロン) で OR 条件を記述できます。
--gtest_filter=*Constructor*:*Copy* で、Constructor または Copy を含むテストを実行します。

-(マイナス) で除外も可能です。
--gtest_filter=FooTest.*-FooTest.Bar で、FooTest.Bar テストを除く、テストケース FooTest 内のテストを実行します。

--gtest_filter の詳しい使用方法は、上級ガイドのテストを選択するを参照してください。

--gtest_shuffle
このオプションを指定するとテストケース・テストそれぞれランダムな順番で実行されます。
テストが他のテストの結果に依存していないか確認するのに最適です。
乱数のシードは、--gtest_random_seed=<NUMBER> で指定可能です。

--gtest_repeat=<COUNT>
テストを繰り返し実行します。
繰り返し実行させることで、再現性の低いテスト失敗が出るまで繰り返すことができます。
テストで乱数を使っている場合などに有効でしょう。
--gtest_repeat=-1 とすると無限に繰り返すので、
退社するまえにテストを走らせておいて一晩テストをさせることもできます。
また、--gtest_shuffle を指定すると毎回異なるシードでテストを行うので活用してみてください。

※ Google Test 関係の記事一覧はこちら

2012年2月18日土曜日

Google Test で Boost.Test の WARN flavor を実現する

Boost.Test には flavors というものがあり、アサーションのレベルがそれぞれ違います。
もちろん、Google Test にも同等のものが存在します。

エラー:ならない
テストの実行:継続
エラー:なる
テストの実行:継続
エラー:なる
テストの実行:中断
Google Test---EXPECTASSERT
Boost.TestWARNCHECKREQUIRE

ご覧のように Google Test には、"エラーにならないアサーション" がありません。
そこで、必要かどうかはさておき Google Test にもエラーにならないアサーションを提供したいと思います。

ベースとなるマクロ
まずは、アサーションで失敗が発生したときに失敗したことにならないようにします。
EXPECT をベースに作っていきましょう。
// GTEST_EXPECT_FAILURE_ をベースに作成
#define GTEST_INFORM_FAILURE_(message) \
 GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess)
                                // kSuccess とし成功を指定する

アサーションは最終的に GTEST_<level>_FAILURE_ を呼ぶので、エラーにならないアサーション用に GTEST_INFORM_FAILURE_ を定義します。(エラーにならないアサーションを INFORM としています。)

述語アサーション
次に、ほとんどのアサーションが述語アサーションを利用しているので、そちらを用意します。
#define INFORM_PRED_FORMAT1(pred_format, v1) \
  GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_INFORM_FAILURE_)
#define INFORM_PRED1(pred, v1) \
  GTEST_PRED1_(pred, v1, GTEST_INFORM_FAILURE_)

#define INFORM_PRED_FORMAT2(pred_format, v1, v2) \
  GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_INFORM_FAILURE_)
#define INFORM_PRED2(pred, v1, v2) \
  GTEST_PRED2_(pred, v1, v2, GTEST_INFORM_FAILURE_)
GTEST_INFORM_FAILURE_ を使うように書き換えただけです。
(述語アサーションは "5" まであるので、すべて用意します。)

各アサーションの定義
最後に、INFORM_TRUE とか INFORM_EQ マクロを定義します。
先に定義した述語アサーションを使うようにするだけです。
#define INFORM_TRUE(condition) GTEST_TEST_BOOLEAN_(condition, #condition \
    , false, true, GTEST_INFORM_FAILURE_)

#define INFORM_EQ(expected, actual) \
  INFORM_PRED_FORMAT2(::testing::internal:: \
   EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
   expected, actual)
#define INFORM_NE(expected, actual) \
  INFORM_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual)
#define INFORM_LE(val1, val2) \
  INFORM_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
#define INFORM_LT(val1, val2) \
  INFORM_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)
// 他にもありますが、ここでは省略します。

使い方
他のアサーションと同様に使います。
TEST(InfromTest, Sample)
{
    INFORM_TRUE(rand() != 0);
    INFORM_EQ(5, rand());
}

出力イベントリスナー
さて、ここまで順調にきましたが 問題 が発生します。
Google Test デフォルトの出力イベントリスナーだと何も出力されません。
TestPartResult::kSuccess のときは、何もせず return するようにコーディングされているからです。

そこで、ユーザー定義のイベントリスナーを追加してやります。

class InformPrinter : public ::testing::EmptyTestEventListener
{
private:
 virtual void OnTestPartResult(const TestPartResult& test_part_result)
 {
  // 成功のときに出力
  if( test_part_result.type() == TestPartResult::kSuccess )
  {
   // 出力してもらえるようにダミーのタイプを設定する
   TestPartResult tmp(TestPartResult::kNonFatalFailure
    , test_part_result.file_name()
    , test_part_result.line_number()
    , test_part_result.message());
   ::testing::UnitTest::GetInstance()->
    listeners().default_result_printer()->OnTestPartResult(tmp);
  }
 }
};

int main(int argc, char **argv) {
 testing::InitGoogleTest(&argc, argv);
 // InformPrinter をイベントリスナーに追加
 ::testing::UnitTest::GetInstance()->
  listeners().Append( new InformPrinter() );
 return RUN_ALL_TESTS();
}

iutest でできるようにしました
ここまで紹介してきた INFORM マクロを iutest で使えるようにしました。(v0.18.1.0 以降)
iutest は自作の C++ テストフレームワーク です。
Boost.Test には、include するだけ使える方法もあるようですが、
iutest を使えば、include するだけで Google Test の記法を使うことができます。
詳しくは、 SourceForge.Jp の プロジェクトページ を見てください。

2012年2月14日火曜日

「Boost.勉強会 #8 大阪」に参加してきました

というわけで、Jenkins 勉強会に引き続き、大阪に行って参りました。

どれも非常に興味深い(そして難しい)お話が聞けて、とても良かったです。

色々なお話がありましたが、個人的に一番興味のあったテスト関係の感想を。
まぁ、テストとかテストとかテストとかやってる身なので…
UST の URL はこちら。
C++でテスト駆動開発 (UST が見当たらなかったので SlideShare)
Boost.Testの紹介

C++でテスト駆動開発
TDD の話は、Aimingさんの社内勉強会 UST を事前に見てたので、フムフムと再確認。
Google Mock は、使ったことないので調べてみようと思いました。
(とりあえず、自分用に日本語ドキュメントの URL を貼っときます。Google Mock ドキュメント日本語訳

Boost.Testの紹介
Boost.Test の存在は前々から知っていましたが、
Google Test で運用を始めていたのと、それで不満なく使えていたのでスルーしてました。
あとは、パッと見使い方がわからなかった。。。

正直、Google Test でいいと思うが、Boost.Test を知らずして
Google Test 最高!とも言えないので、ちゃんと使ってみよう!

って、意気込んだものの忙しくて何もできてません・・・
そのへんはいずれ記事にできたらと思います。