[MEMO] Google Testing Framework
2009.01.18 Sunday 23:14
もう半年も前の話だが,Google C++ Testing Frameworkというのが公開されていたようだ.これはCppUnitと同様に単体テストを行うものだが,CppUnitに比べて以下のような利点がある.
1. テスト関数を別途列挙する必要がない
2. 例外を使わない
2-a. std::string等に依存しない.
1. テスト関数を別途列挙する必要がない
2. 例外を使わない
2-a. std::string等に依存しない.
まず1について
C++にはリフレクション機構が存在しないので,実行時に関数名一覧を取得することができない.代わりにstatic変数の初期化がmain()の前に動作することを利用する.
CppUnitではテストをTestFixtureのメンバ関数として作成するので,TestFixtureの宣言部において関数の一覧を以下のように記述する必要がある.
さらに,TestFixtureはCPPUNIT_TEST_SUITE_REGISTRATIONを用いてインスタンス化する必要がある.
以上の2つを忘れるとテストコードを書いたつもりでもテストが実行されない.「やったー,テストOKだー」と思っても実はそのコードが実行されてないと後でしょんぼり.
参考: CppUnit 導入ガイド
Google C++ Test Frameworkではこの問題を以下のようにして解決している.
各テストはTESTマクロ(Fixtureを使わない場合),またはTEST_F(Fixtureを使用する場合)を用いて宣言するが,このマクロは次のような構造になっている.
[include/gtest/gtest.h]
そしてGTEST_TEST_はinclude/gtest/gtest-internal/gtest-internal.hで定義されている.長いのでエッセンスだけ言葉で書くと,
1. テストごとに独立したクラスを定義する
2. そのクラスはTestFixtureまたはTestを継承する.
3. そのクラスはtest_info_という名のstaticメンバを持つ.
4. クラス定義の直後にtestInfoの初期化があり,その代入文においてMakeAndRegisterTestInfo()を呼び出して自分自身を登録するようになっている.
また,上の呼び出し時にGetTestTypeId<>()を呼んでいるが,これは型毎に異なる値を返す関数で,TypeIdHelper<型名>テンプレートクラスの中で宣言されたstaticメンバの先頭アドレスとして実装されている.
次に2について.
CppUnitではアサーションに引っかかると例外(Exception)をthrowし,テストコードの呼び出し元でそれをcatchしている.
(Asserter.cppのAsserter::fail及びDefaultProtector.cppのDefaultProtector::protectを参照)
Google C++ Test Frameworkでは大きく分けてASSERT_*とEXPECT_*の2種類の評価方法が用意されている.ホームページに書かれた説明によると「両者の違いは評価結果の扱いで,前者は評価にパスしない場合は処理が打ち切られるのに対し、後者は結果にかかわらず処理が継続される」はずなのだが,ソースを見る限りでは両者の違いはFATAL/NON-FATALというフラグ値の違いだけのように見える.(後で実行して試す必要有り)
UnitTest::AddTestPartResultにおいて,結果がSUCCESSではなくてGTEST_FLAG(break_on_failure)が設定されている場合に強制的にsegmentation faultを発生させるコードはあったが,これは上記区別とは無関係である.
これらの実装でAssertHelperというクラスが使われている.このクラスは評価のたびに一時オブジェクトとして生成され,operator=でtesting::Message()オブジェクトを受け取る.このoperator=は実際にはAssertHelper自体への代入ではなく,UnitTest::GetInstance()->AddTestPartResult()にデータを渡しているので代入の意味からは外れているが,後ろに<<を続けて標準入出力のスタイルでユーザメッセージを追加可能とするためにこうなっている.
C++にはリフレクション機構が存在しないので,実行時に関数名一覧を取得することができない.代わりにstatic変数の初期化がmain()の前に動作することを利用する.
CppUnitではテストをTestFixtureのメンバ関数として作成するので,TestFixtureの宣言部において関数の一覧を以下のように記述する必要がある.
CPPUNIT_TEST_SUITE(TestFixtureクラス名);
CPPUNIT_TEST(テスト関数名);
CPPUNIT_TEST_SUITE_END();
さらに,TestFixtureはCPPUNIT_TEST_SUITE_REGISTRATIONを用いてインスタンス化する必要がある.
以上の2つを忘れるとテストコードを書いたつもりでもテストが実行されない.「やったー,テストOKだー」と思っても実はそのコードが実行されてないと後でしょんぼり.
参考: CppUnit 導入ガイド
Google C++ Test Frameworkではこの問題を以下のようにして解決している.
各テストはTESTマクロ(Fixtureを使わない場合),またはTEST_F(Fixtureを使用する場合)を用いて宣言するが,このマクロは次のような構造になっている.
[include/gtest/gtest.h]
#define TEST(test_case_name, test_name)\
GTEST_TEST_(test_case_name, test_name,\
::testing::Test, ::testing::internal::GetTestTypeId())
#define TEST_F(test_fixture, test_name)\
GTEST_TEST_(test_fixture, test_name, test_fixture,\
::testing::internal::GetTypeId())
そしてGTEST_TEST_はinclude/gtest/gtest-internal/gtest-internal.hで定義されている.長いのでエッセンスだけ言葉で書くと,
1. テストごとに独立したクラスを定義する
2. そのクラスはTestFixtureまたはTestを継承する.
3. そのクラスはtest_info_という名のstaticメンバを持つ.
4. クラス定義の直後にtestInfoの初期化があり,その代入文においてMakeAndRegisterTestInfo()を呼び出して自分自身を登録するようになっている.
また,上の呼び出し時にGetTestTypeId<>()を呼んでいるが,これは型毎に異なる値を返す関数で,TypeIdHelper<型名>テンプレートクラスの中で宣言されたstaticメンバの先頭アドレスとして実装されている.
次に2について.
CppUnitではアサーションに引っかかると例外(Exception)をthrowし,テストコードの呼び出し元でそれをcatchしている.
(Asserter.cppのAsserter::fail及びDefaultProtector.cppのDefaultProtector::protectを参照)
Google C++ Test Frameworkでは大きく分けてASSERT_*とEXPECT_*の2種類の評価方法が用意されている.ホームページに書かれた説明によると「両者の違いは評価結果の扱いで,前者は評価にパスしない場合は処理が打ち切られるのに対し、後者は結果にかかわらず処理が継続される」はずなのだが,ソースを見る限りでは両者の違いはFATAL/NON-FATALというフラグ値の違いだけのように見える.(後で実行して試す必要有り)
UnitTest::AddTestPartResultにおいて,結果がSUCCESSではなくてGTEST_FLAG(break_on_failure)が設定されている場合に強制的にsegmentation faultを発生させるコードはあったが,これは上記区別とは無関係である.
これらの実装でAssertHelperというクラスが使われている.このクラスは評価のたびに一時オブジェクトとして生成され,operator=でtesting::Message()オブジェクトを受け取る.このoperator=は実際にはAssertHelper自体への代入ではなく,UnitTest::GetInstance()->AddTestPartResult()にデータを渡しているので代入の意味からは外れているが,後ろに<<を続けて標準入出力のスタイルでユーザメッセージを追加可能とするためにこうなっている.
Comments