Google C++ユニットテストフレームワーク(Gtest)シリーズチュートリアルの3-テストファームウェア(Test fixture)

8775 ワード

引用する
『Google C++ユニットテストフレームワーク(Gtest)シリーズチュートリアルの2——断言、関数テスト』では,断言文とTEST()を用いて関数テストを行う方法を理解し,TEST()の使用では,1つのテスト例が複数のテスト例を含む組織方式に触れた.複数のテスト・インスタンスでは、既知のデータ構成と初期化操作が必要になる場合があります.そのため、Gtestはテスト・ファームウェア(Test fixture)を提供し、データ管理を支援します.
「立ち後れ」の方法
テストファームウェアを理解する前に、次のテスト例を見てみましょう.
template <typename E> // E is the element type.
class Queue {
public:
Queue();
void Enqueue(const E& element);
E* Dequeue(); // Returns NULL if the queue is empty.
size_t size() const;
...
};

以上のQueueクラスをテストすると仮定し、以前に学習したTEST()の使い方に基づいて、テストコードを以下のように記述します.
//     
TEST(QueueTest, IsEmptyInitially) {
  Queue<int> q0_;
EXPECT_EQ(0, q0_.size());
}
TEST(QueueTest, DequeueWorks) {
  Queue<int> q0_;
 
Queue<int> q1_;
 
Queue<int> q2_;

  q1_.Enqueue(1);
  q2_
.Enqueue(2);
  q2_
.Enqueue(3);

int* n = q0_.Dequeue();
EXPECT_EQ(NULL, n);

n = q1_.Dequeue();
ASSERT_TRUE(n != NULL);
EXPECT_EQ(1, *n);
EXPECT_EQ(0, q1_.size());
delete n;

n = q2_.Dequeue();
ASSERT_TRUE(n != NULL);
EXPECT_EQ(2, *n);
EXPECT_EQ(1, q2_.size());
delete n;
}

あなたはすでに問題の所在を発見しましたか?はい、赤いフォントのテストデータ初期化部分に重複コードがあります!この例には2つのテストインスタンスしか含まれていません.重複コードの問題は際立っていませんが、数十個以上のテストインスタンスでは、初期化データを管理する別の方法が必要です.
テストファームウェア(Test fixture)
テストファームウェアの役割は、2つ以上のテストインスタンスで使用されるデータを管理し、テストファームウェアを使用して上記のテストを完了する方法です.
まず、一般的なファームウェアクラスはFooTestとして命名され、Fooは被測定クラスの名前であるファームウェアクラス(fixture class)を定義する必要があります.
class QueueTest : public ::testing::Test {
protected:
virtual void SetUp() {
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// virtual void TearDown() {}
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};

ファームウェアクラスを定義する方法は、次のとおりです.
  • は、クラスのサブクラスがクラスのデータにアクセスできるようにpublicまたはprotectedをアクセス制御識別として使用する、test::Testから継承されたクラスを書く.
  • このクラスでは、テストインスタンスが使用するデータを定義する.
  • SetUp()メソッドまたはデフォルトコンストラクション関数を使用してデータ初期化操作を行い、TearDown()メソッドまたはコンストラクション関数を使用してデータクリーンアップ操作を行い、SetUp()とTearDown()のスペルに注意する.
  • 必要に応じて、初期化データのようにクラスにメンバー関数を定義することもできます.ここで定義したメンバー関数は、テストインスタンスによって繰り返し使用することもできます.

  • 次に、対応するテストインスタンスを作成する方法について説明します.まず、新しいマクロを使用します.
    TEST_F(test_case_name, test_name) {
    ... test body ...
    }

    TEST_F()は、2つのパラメータの意味がTEST()のパラメータの意味と同じであるが、TEST_F()の最初のパラメータは、ファームウェアクラスの名前である必要があります.
    上記QueTestテストファームウェアと組み合わせて、テストコードを以下のように作成します.
    //     
    TEST_F(QueueTest, IsEmptyInitially) {
    EXPECT_EQ(0, q0_.size());
    }

    TEST_F(QueueTest, DequeueWorks) {
    int* n = q0_.Dequeue();
    EXPECT_EQ(NULL, n);

    n = q1_.Dequeue();
    ASSERT_TRUE(n != NULL);
    EXPECT_EQ(1, *n);
    EXPECT_EQ(0, q1_.size());
    delete n;

    n = q2_.Dequeue();
    ASSERT_TRUE(n != NULL);
    EXPECT_EQ(2, *n);
    EXPECT_EQ(1, q2_.size());
    delete n;
    }

    TEST_がわかりますF()の使用方法はTEST()と大きく異なりません.上記の2つのテストインスタンスが実行されると、Gtestは以下のことをしました.
  • QueTestオブジェクト(t 1と仮定)を構築する.
  • はt 1を呼び出す.SetUp()は、t 1オブジェクトを初期化する.
  • の最初の試験例(IsEmptyInitially)は、t 1を用いて試験される.
  • はt 1を呼び出す.TearDown()はデータの整理を行う;
  • 廃棄対象t 1;
  • 新しいQueTestオブジェクトを作成し、次のテストインスタンスDequeueWorksについて上記の手順を繰り返します.

  • Gtestは、ファームウェアクラスオブジェクトを作成および破棄することによって、各テストインスタンスに独立した初期化データを作成します.上記の2つのテストスキームの目的と結果はまったく同じですが、シナリオ2は、テストファームウェアを使用することによって、データ初期化による重複コードを根絶します.
    ファームウェアクラス(Fixture class)
    C++クラスは継承可能な特徴を持っており、このようにファームウェアクラスを柔軟に定義することができ、複数のファームウェアクラスが共有する特性を抽象化してベースクラスを形成し、コード多重化、データ多重化の効果をさらに達成することができ、次の例を見てみましょう.
    class QuickTest : public testing::Test {
    protected:
    // This is a good place to record the start time.
    virtual void SetUp() {
    start_time_ = time(NULL);
    }
    // check if the test was too slow.
    virtual void TearDown() {
    // Gets the time when the test finishes
    const time_t end_time = time(NULL);
    // Asserts that the test took no more than ~5 seconds.
    EXPECT_TRUE(end_time - start_time_ <= 5) << "The test took too long.";
    }

    このファームウェアクラスは、テストインスタンスの実行時間を簡単に分析し、SetUp()がテストインスタンスの実行前に実行し、TearDown()がテストインスタンスの実行後に実行するという特徴を利用し、実行時間が5秒を超えるテストインスタンスは検出に失敗します.SetUp()とTearDown()関数でも断言文を使用することができることに注意してください.
    Queueクラスのテストインスタンスに実行時間制限があると仮定すると、QuickTestから継承されたファームウェアクラスを記述できます.
    class QueueTest : public QuickTest {
    //......
    };

    このように定義すると、QueTestに関連付けられたテストインスタンスが実行されると、その実行時間が検出されます.
    小結
    Gtestテストファームウェア(Test fixture)を用いた理由と方法を紹介し,最後にクラス継承によりテストファームウェアを柔軟に定義できることを提案した.次のセクションでは、Gtest値のパラメータ化、タイプのパラメータ化の使用方法について説明します.Reference: googletest project