2015年6月16日火曜日

[C++] Private な関数のテスト

本記事で扱うテスティングフレームワーク
Google Test
iutest (Google Test ライクなヘッダーオンリーテスティングフレームワーク)

C++ でテスト書いていると問題になるのが、private なメンバー関数をどうテストするか、です。
成功法としては、テストクラスを friend することです。
間違っても #define private public などとしてはいけません。

friend
Google Test の場合、テストクラスを friend する FRIEND_TEST マクロが用意されています。
また、テストフィクスチャクラスに対して friend 宣言すれば、そのテストケースではアクセス可能になります。

参照: GoogleTestでprivateメンバ関数をテストする | Geospatial屋
詳しいことは参照先を見ていただくとして、簡単に使い方を例として示します。

例1: FRIEND_TEST
// テスト対象クラス
class Hoge
{
   FRIEND_TEST(HogeTest, A); // TEST(HogeTest, A) から見えるようにする
   FRIEND_TEST(HogeTest, B); // TEST(HogeTest, B) から見えるようにする
private:
   int GetA() { return 1; }
   int GetB() { return 2; }
};

// テスト
TEST(HogeTest, A)
{
   Hoge hoge;
   ASSERT_EQ(1, hoge.GetA());
}
TEST(HogeTest, B)
{
   Hoge hoge;
   ASSERT_EQ(2, hoge.GetB());
}
テスト毎に FRIEND_TEST しないといけないので、ちょっと面倒ではありますが private メンバーにアクセスできるようになります。
例では書きませんでしたが、テストフィクスチャを使った場合(TEST_F)でも使用できます。
(※ 型付けテストでは使用できません。)

例2: friend TestFixture
2番目の方法は、テストフィクスチャクラスを friend する方法です。
// テスト対象クラス
class Hoge
{
   friend class HogeTest; // HogeTest フィクスチャーに対して friend
private:
   int GetA() { return 1; }
   int GetB() { return 2; }
};

// テスト
class HogeTest : public ::iutest::Test 
{
protected:
    Hoge hoge;
    // アクセサを書く
    int GetA() { return hoge.GetA(); }
    int GetB() { return hoge.GetB(); }
};
TEST_F(HogeTest, A)
{
    ASSERT_EQ(1, GetA());
}
TEST_F(HogeTest, B)
{
    ASSERT_EQ(2, GetB());
}
テストフィクスチャが必要になるので、TEST(Hoge, A) のようなテストには使えませんが、
FRIEND_TEST の時とは違ってテストケースすべてに対してアクセスを許可することができるので、使い勝手は良いです。

ただ、テストフィクスチャクラスでアクセサを書く必要があります。これは Google Test がテスト毎にテストフィクスチャを継承したクラスを定義しているからです。
とはいえ、一度テストフィクスチャを定義してしまえば使い回しが可能ですし、テストコード側の変更だけでテストを追加できるのが、この方法のメリットでしょう。

iutest でも、もちろん上記方法が使えます。

Google Test の問題
Google Test が用意している FRIEND_TEST マクロは、型付けテストには使えません
template<typename T> をつければいいだけなので、以下のようにマクロを追加すれば OK です。

#define FRIEND_TYPED_TEST template<typename T>FRIEND_TEST

iutest は、FRIEND_TYPED_TEST に対応済みです。

プロダクトコードに変更を加えない方法
さて、friend を使った方法は、プロダクトコード側に friend 宣言をする必要がありました。
その程度の記述は大したことないかもしれませんが、やはりプロダクトコードを1行も変えずにアクセスできたら便利ですよね。
(テスト名を書かないといけないのも個人的には面倒だなと思います。)

iutest では非侵入的に private メンバーにアクセスする方法を提供しています。


// テスト対象クラス
class Hoge
{
private:
   int GetA() { return 1; }
   int GetB() { return 2; }
};

typedef int (Hoge::* HogePrivateFunc)(); // iutest v1.12.0 からは typedef 不要になります
IUTEST_MAKE_PEEP(HogePrivateFunc, Hoge, GetA);
IUTEST_MAKE_PEEP(HogePrivateFunc, Hoge, GetB);

// テスト
IUTEST(HogeTest, A)
{
   Hoge hoge;
   IUTEST_ASSERT_EQ(1, IUTEST_PEEP_GET(hoge, Hoge, GetA)());
}
IUTEST(HogeTest, B)
{
   Hoge hoge;
   IUTEST_ASSERT_EQ(1, IUTEST_PEEP_GET(hoge, Hoge, GetB)());
}

なんか記述量増えてるし、マクロやばそうと思われる方もいると思いますが、
この方法であれば、完全にプロダクトコードを汚すことなく private なメンバーのテストが書けます。

private メンバーにアクセスする方法は、こちらで紹介されている方法を使いました。
privateメンバに外部から非侵入的にアクセスする - redboltzの日記

いや~凄いですね。
さて、来週に iutest v1.12.0 のリリースを予定してます。
正規表現アサーションの他、いろいろ更新されてますのでよろしくお願いします。

0 件のコメント:

コメントを投稿