Google Test C++の使い方(初級)
やりたいこと
C++のテストがしたい。
Google Testがいい感じという噂。
教科書
入門まとめ
基本的なアサーション
ASSERT_TRUE(condition); EXPECT_TRUE(condition); condition が true
ASSERT_FALSE(condition); EXPECT_FALSE(condition); condition が false
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length"; for (int i = 0; i < x.size(); ++i) { EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i; }
簡単なテスト
プロダクトコード
int Factorial(int n); // nの階乗を返す
テストコード
// 0 の階乗をテスト TEST(FactorialTest, HandlesZeroInput) { EXPECT_EQ(1, Factorial(0)); } // 正の数の階乗をテスト TEST(FactorialTest, HandlesPositiveInput) { EXPECT_EQ(1, Factorial(1)); EXPECT_EQ(2, Factorial(2)); EXPECT_EQ(6, Factorial(3)); EXPECT_EQ(40320, Factorial(8)); }
テストフィクスチャ:複数のテストで同じデータ設定を使う
プロダクトコード
template <typename E> // E は要素の型 class Queue { public: Queue(); void Enqueue(const E& element); E* Dequeue(); // queue が空の場合は NULL を返します. size_t size() const; ... };
テストのフィクスチャ
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_; };
このフィクスチャとTEST_F() を利用したテストコード
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; }
テストの呼び出し
テストを定義したら,RUN_ALL_TESTS() でテストを実行できる
#include "this/package/foo.h" #include "gtest/gtest.h" namespace { // テスト対象となるクラス Foo のためのフィクスチャ class FooTest : public ::testing::Test { protected: // 以降の関数で中身のないものは自由に削除できます. // FooTest() { // テスト毎に実行される set-up をここに書きます. } virtual ~FooTest() { // テスト毎に実行される,例外を投げない clean-up をここに書きます. } // コンストラクタとデストラクタでは不十分な場合. // 以下のメソッドを定義することができます: virtual void SetUp() { // このコードは,コンストラクタの直後(各テストの直前) // に呼び出されます. } virtual void TearDown() { // このコードは,各テストの直後(デストラクタの直前) // に呼び出されます. } // ここで宣言されるオブジェクトは,テストケース内の全てのテストで利用できます. }; // Abc を行う Foo::Bar() メソッドをテストします. TEST_F(FooTest, MethodBarDoesAbc) { const string input_filepath = "this/package/testdata/myinputfile.dat"; const string output_filepath = "this/package/testdata/myoutputfile.dat"; Foo f; EXPECT_EQ(0, f.Bar(input_filepath, output_filepath)); } // Xyz を行う Foo をテストします. TEST_F(FooTest, DoesXyz) { // Foo の Xyz を検査 } } // namespace int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
Mockまとめ
わからないことがあったとき
基本ステップ
簡単なマクロを使ってモック化したいインタフェースを記述します.これが,モッククラスの実装に展開されます.
モックオブジェクトを作成し,直感的な構文を利用して Expectation と 動作を規定します.
モックオブジェクトを利用するコードを実行します.Google Mock は,Expectation 違反が起こると即座にそれをキャッチします.
使うための準備
C++ ソースファイル内で, “gtest/gtest.h” と “gmock/gmock.h” を #include するだけで準備は終わり。
モック理解のためのサンプル
class Turtle { ... virtual ~Turtle() {} virtual void PenUp() = 0; virtual void PenDown() = 0; virtual void Forward(int distance) = 0; virtual void Turn(int degrees) = 0; virtual void GoTo(int x, int y) = 0; virtual int GetX() const = 0; virtual int GetY() const = 0; };
モックの定義
Turtle から MockTurtle クラスを派生させます.
Trutle の仮想関数を調べて,引数の数を数えます.
派生クラスの public セクションに,MOCK_METHODn();(const メソッドをモック化する場合は, MOCK_CONST_METHODn();)を書きます.ここで n は,引数の数を表しますが,これを数え間違えると,コンパイルエラーで注意されます. 関数のシグネチャを調べて,その関数名をマクロの1番目の引数に,残りを2番目の引数にします.
これを,モック化したい仮想関数全てに対して繰り返します.
MOCK_METHOD0 のように最後の数字は引数の数を指定。
#include "gmock/gmock.h" // Google Mock はこのヘッダに. class MockTurtle : public Turtle { public: ... MOCK_METHOD0(PenUp, void()); MOCK_METHOD0(PenDown, void()); MOCK_METHOD1(Forward, void(int distance)); MOCK_METHOD1(Turn, void(int degrees)); MOCK_METHOD2(GoTo, void(int x, int y)); MOCK_CONST_METHOD0(GetX, int()); MOCK_CONST_METHOD0(GetY, int()); };
モックの使い方
モックオブジェクトを作成します.
その Expectation を設定します
モックを利用したコードを実行します.Google Test のアサーションを利用して結果のチェックを行っても良いでしょう.モックメソッドが期待よりも多く呼び出される,または引数が誤っている,などの場合は,即座にエラーが起こります.
モックがデストラクトされると,その全ての Expectation が満足されたかどうかを Google Mock が自動的に検証します.
#include "path/to/mock-turtle.h" #include "gmock/gmock.h" #include "gtest/gtest.h" using ::testing::AtLeast; // #1 TEST(PainterTest, CanDrawSomething) { MockTurtle turtle; // #2 EXPECT_CALL(turtle, PenDown()) // #3 .Times(AtLeast(1)); Painter painter(&turtle); // #4 EXPECT_TRUE(painter.DrawCircle(0, 0, 10)); } // #5 int main(int argc, char** argv) { // 以下の行は,テスト開始前に Google Mock (と Google Test) // を初期化するために必ず実行する必要があります. ::testing::InitGoogleMock(&argc, argv); return RUN_ALL_TESTS(); }
Exceptionの作り方
Expectation を設定するために EXPECT_CALL() マクロを使う。
このマクロには2つの引数があり、1番目はモックオブジェクトで,2番目はメソッドと引数。
using ::testing::Return;... EXPECT_CALL(turtle, GetX()) .Times(5) .WillOnce(Return(100)) .WillOnce(Return(150)) .WillRepeatedly(Return(200));
turtle オブジェクトの GetX() メソッドが5回呼ばれ,最初は 100 を返し,次に 150,それ以降は 200 を返す,という動作のテスト。
所感
unittestに似てる。書きながら使えるようになろう~