読者です 読者をやめる 読者になる 読者になる

googletestに付属するサンプルを読み解く

C++

このエントリーをはてなブックマークに追加

こないだの続き。こないだは、samplesフォルダにあるsample1_unittestをビルドして、単体テストできていることを確かめた。今回は、その他のsample2〜sample10を同様にビルドして、sample1〜sample6の中身を読み解いてみる。sample7〜sample10は、残念ながら私の能力が足りず読解を断念。

サンプルをすべてビルド

まず、READMEに従って、googletestのスタティックライブラリを作成する。

  • $ gtest-1.7.0.zipを展開
  • $ cd gtest-1.7.0
  • $ mkdir lib
  • $ g++ -isystem ../include -I../ -pthread -c ../src/gtest-all.cc
  • $ ar -rv libgtest.a gtest-all.o


次にサンプルをビルドする。cd ../src/して、以下のスクリプトを作成して実行する。(Makefileでなくて申し訳ない)

#!/bin/sh

# sample 1, 2, 4
for i in {1,2,4}
do
g++ -L../lib -isystem ../include -g -Wall -Wextra -pthread \
    sample${i}.cc sample${i}_unittest.cc ../src/gtest_main.cc -lgtest -o sample${i}_unittest
done


# sample 3, 6, 7, 8
for i in {3,6,7,8}
do
    g++ -L../lib -isystem ../include -g -Wall -Wextra -pthread \
        sample${i}_unittest.cc ../src/gtest_main.cc -lgtest -o sample${i}_unittest
done


# sample 5
g++ -L../lib -isystem ../include -g -Wall -Wextra -pthread \
    sample5_unittest.cc sample1.cc ../src/gtest_main.cc -lgtest -o sample5_unittest


# sample 9, 10
for i in {9,10}
do
    g++ -L../lib -isystem ../include -g -Wall -Wextra -pthread \
        sample${i}_unittest.cc -lgtest -o sample${i}_unittest
done

sample1_unittest

これは前回の記事で見たとおり。Cスタイルの非メンバ関数をテストするサンプル。

sample2_unittest

自作文字列クラスであるMyStringクラスの、publicな関数をテストするサンプル。

MyStringクラスはsample2.hとsample2.ccに実装されている。

class MyString {
 public:
  // publicな関数の例(コンストラクタ)
  MyString() : c_string_(NULL) {}
 …
}

例えば、このコンストラクタをテストするテストケースは、sample2_unittest.ccで以下のように書かれている。

// Tests the default c'tor.
TEST(MyString, DefaultConstructor) {
  const MyString s;

  EXPECT_STREQ(NULL, s.c_string());

  EXPECT_EQ(0u, s.Length());
}

sample3_unittest

test fixture(テストフィクスチャ)と呼ばれる仕組みが登場する。test fixtureを使うと、いくつかのテストを行う前に、自動で条件を揃えることができる。

例を見てみる。sample3-inl.hでは、自作キュークラスが実装されている。sample3_unittest.ccはそのキューのテストを行っている。キューをテストするためにはキューに値をつっこまないとテストできないわけだが、テストケースごとに毎回値をつっこむのは非効率的である。そこでtest fixtureの出番となる。

test fixtureを使うためには、testing::Testクラスを継承したクラスを作成する。

class QueueTest : public testing::Test {
 protected:
  virtual void SetUp() {
    // テストケースの最初に毎回実行したいことを書く
    q1_.Enqueue(1);
    q2_.Enqueue(2);
    q2_.Enqueue(3);
  }

  virtual void TearDown() {
    // テストケースの最後に毎回実行したいことを書く
    // したいことがないならコメントアウトすればよい
  }

  // テストケースで使いたい便利関数を定義してもよい
  static int Double(int n) {
    return 2*n;
  }


TEST_Fマクロを使ってテストケースを作成すると、テストケースの冒頭にSetUp()が、テストケースの末尾にTearDown()が自動実行される。

TEST_F(QueueTest, DefaultConstructor) {
  // You can access data in the test fixture here.
  EXPECT_EQ(0u, q0_.Size());
}

test fixtureについての日本語の詳しい説明は入門ガイド — Google Test ドキュメント日本語訳を参照。

ちなみに、test fixtureは直訳で「試験装置」という意味らしい。

sample4_unittest

Increment()を呼ばれるたび内部変数をインクリメントするCounterクラスをテストするサンプル。テスト用マクロのEXPECT_EQ()内でメソッドを呼ぶと、このメソッドが評価されて副作用が起きますよ、ということを説明するためのサンプルらしい。

TEST(Counter, Increment) {
  Counter c;
  EXPECT_EQ(0, c.Increment());
  EXPECT_EQ(1, c.Increment());
  EXPECT_EQ(2, c.Increment());
}

sample5_unittest

継承を使って、少しずつ異なるtest fixtureを作る方法を説明するサンプル。このサンプルでは、QuickTestという、継承元となるスーパークラス(super fixture)を作成している。QuickTestでは、各テストが一定時間内に終わったかどうかを測定するコードがSetUp()とTearDown()に仕組まれている。

class QuickTest : public testing::Test {
 protected:
  virtual void SetUp() {
    start_time_ = time(NULL);
  }

  virtual void TearDown() {
    const time_t end_time = time(NULL);
	// 処理が5秒より多くかかるならばfailとする
    EXPECT_TRUE(end_time - start_time_ <= 5) << "The test took too long.";
  }

  time_t start_time_;
};


QuickTestクラスは、IntegerFunctionTestクラスとQueueTestクラスとに継承される。IntegerFunctionTestクラスやQueueTestクラスをtest fixtureとしてテストケースを作ることで、そのテストが5秒以内に終わることをテストの条件に含めることができる。

sample6_unittest

このサンプルでは、ある抽象クラスを継承して作成した複数の具象クラスに対して、まとめてテストを行う方法が紹介されている。

1つ目の方法は、testing::Typesを用いた"typed test"(=型付けテスト?)。テストしたい型を、Types<と>の間に列挙する。

typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable> Implementations;
TYPED_TEST_CASE(PrimeTableTest, Implementations);

2つ目の方法は、"type-parameterized test"(=型をパラメータ化したテスト?)。正直よく分からない…

sample6を理解するためには、ブログズミ: Google Test を使ってみる - その3(テストケース)を読む方が良いと思う。