GoogleTest(gTest)


テスト駆動開発(TDD)について

eclipse導入手順(Cygwinの場合)

  1. サイトよりダウンロード
    http://code.google.com/p/googletest/
  2. Cygwinを起動して、フォルダ作成.フォルダ配下にダウンロードしたファイルを置く。
    mkdir gtest
  3. gtestをコンパイルして、gtest-all.oを作る。
    cd /cygdrive/c/src/gtest
    ./configure
    make
    [memo]
    コンパイラのバージョンをあげたらコンパイルしなおす(ハマったので)
  4. eclipseでC++プロジェクト新規作成
    ([実行可能プロジェクト] > [空のプロジェクト]でつくる。
    かってにフォルダを探してMakefileに追加する機能がうれしいので)
  5. インクルードパスの指定
    1. プロジェクトを右クリック > [プロパティ] > [C/C++ビルド]>設定を選択
    2. Cygwin C++ Compilerのインクルードを選択
    3. インクルード・パス(-I)で以下のパスを指定
      "/cygdrive/c/src\gtest\include"
      "/cygdrive/c/src\gtest"
      "C:\src\gtest\include"
      "C:\src\gtest"
      (/cygdrive/cと C:\は両方書いておく。しかし、/cygdrive/c/を上に持ってくる
      理由はよくわからないけれども、これでうまくいったので)
  6. オブジェクトファイルの指定
    1. プロジェクトを右クリック > [プロパティ] > [C/C++ビルド]>[設定]を選択
    2. Cygwin C++ Linker > その他 を選択し、
      その他のオブジェクトに以下のオブジェクトを追加
       "/cygdrive/c/src\gtest\src\gtest-all.o"
  7. 新規>ソースフォルダを作成
    (ソースフォルダでフォルダを作成することで、
    eclipseが自動的にソースをサーチしてMakefileを作ってくれる)
    tests(テストソース用)
    src(自分のソース用)
  8. ソースファイル(RunAllTests?.cpp)を./tests配下に以下のように作成
    #include "gtest/gtest.h"
    
    int main(int argc, char* argv[])
    {
        ::testing::InitGoogleTest(&argc, argv);
        return RUN_ALL_TESTS();
    }
    ここで、mainファイルを作らない場合は、以下をオブジェクトに追加する
    gtest_main.o
  9. RunAllTests?.cppとgtest-all.oを一緒にコンパイルして、exeファイル作成
    cd Debug
    make
  10. exeファイルを実行して成功。わーい。
    [==========] Running 0 tests from 0 test cases.
    [==========] 0 tests from 0 test cases ran. (0 ms total)
    [  PASSED  ] 0 tests.

eclipse で 自分で(やっぱり上手くいかないので)Makefileを作る方法

  1. 以下で新規作成する。
    [新規] > [C++プロジェクト] > [Makefileプロジェクト] > [空のプロジェクト]
  2. Makefileを以下のように書いて、任意のルートディレクトリに置く。
    (本当に文法があっているかは自信がない)
    # C++なので g++を指定
    CPP  = g++
    
    # 各マクロを定義して、ディレクトリの場所を教える
    PRJ_BASE_DIR  = (任意のルートディレクトリ)
    SRC_DIR       = $(PRJ_BASE_DIR)/src
    TESTS_DIR     = $(PRJ_BASE_DIR)/tests
    GTEST_ROOT    = /cygdrive/c/src/gtest
    
    # インクルードファイル
    LOCAL_INCLUDE  = $(PRJ_BASE_DIR)/src
    # GoogleTest では 以下の2つが必須
    LOCAL_INCLUDE += $(GTEST_ROOT)
    LOCAL_INCLUDE += $(GTEST_ROOT)/include
    
    # ソースファイル
    SRCS    = $(SRC_DIR)/sample.c
    
    # テストファイル
    TEST_SRCS   = $(TESTS_DIR)/RunAllTest.cpp
    TEST_SRCS  += $(TESTS_DIR)/sampleTest.cpp
    
    # OBJ: オブジェクトファイルをまとめるマクロ
    # ${SRCS: .c=.o} は .c を .oへ変換するマクロ
    OBJ  = $(SRCS:.c=.o)
    OBJ += $(TEST_SRCS:.cpp=.o)
    
    # GoogleTest では 以下のgtest-all.oを一緒にコンパイル
    OBJ += $(GTEST_ROOT)/src/gtest-all.o
    
    #コンパイル時のオプションを指定.CPPだとCPPFLAGSが暗黙のフラグ
    CPPFLAGS = $(addprefix -I, $(LOCAL_INCLUDE))
    
    #慣例として、 allをtargetに対して宣言する
    TARGET = sample
    all:$(TARGET)
    
    $(TARGET): $(OBJ) 
    	$(CPP) $(OBJ) -o $(TARGET)
    
    clean:
    	rm -rf $(SRC_DIR)/*.o $(TESTS_DIR)/*.o *.exe
    

GoogleTest?実践例(C言語だよ)

  1. テスト関数を作成(./tests/sampleTest.cpp)
    #include "gtest/gtest.h"
    
    extern "C"
    {
    #include "sample.h"
    }
    
    TEST(AddTest, Test1)
    {
        ASSERT_EQ(2, add(2, 1));
    }
  2. テスト対象関数のヘッダを作成を作成(./src/sample.h)
    int add(int x, int y);
  3. テスト対象関数を作成(./src/sample.c)
    int add(int x, int y)
    {
        return x + y;
    }
  4. ./srcをインクルードパスに追加(導入手順の5参照)
  5. テストを実行
    [==========] Running 1 test from 1 test case.
    [----------] Global test environment set-up.
    [----------] 1 test from AddTest
    [ RUN      ] AddTest.Test1
    ../tests/sampleTest.cpp:17: Failure
    Value of: add(2, 1)
      Actual: 3
    Expected: 2
    [  FAILED  ] AddTest.Test1 (1 ms)
    [----------] 1 test from AddTest (1 ms total)
    
    [----------] Global test environment tear-down
    [==========] 1 test from 1 test case ran. (1 ms total)
    [  PASSED  ] 0 tests.
    [  FAILED  ] 1 test, listed below:
    [  FAILED  ] AddTest.Test1
    
     1 FAILED TEST
  6. あれれ、失敗です。1+2=3ですね。一部修正して再度実行
    TEST(AddTest, Test1)
    {
        ASSERT_EQ(3, add(2, 1));
    }
  7. よかった。成功です。
    [==========] Running 1 test from 1 test case.
    [----------] Global test environment set-up.
    [----------] 1 test from AddTest
    [ RUN      ] AddTest.Test1
    [       OK ] AddTest.Test1 (0 ms)
    [----------] 1 test from AddTest (0 ms total)
    
    [----------] Global test environment tear-down
    [==========] 1 test from 1 test case ran. (0 ms total)
    [  PASSED  ] 1 test.

C言語でも、GoogleTest?は使えます。

キモはテスト関数を.cppでつくり、ヘッダファイルをextern"C"{}で囲むこと

extern "C"
{
#include "xxx.h"
}

比較関数の使い方

EXPECT_EQ(expected, actual)
   expected(期待値) と actual(実際の値)が等しいかをチェック

これ一つ、EXPECT_EQだけ知っていれば、とりあえず困らないのが本音。


テストフィクスチャのつかいかた

  1. 以下のクラスを定義
    クラスとか C++っぽい文法が出てきてもビビらないこと。
    C++っぽいところはここだけだ。
    class addTest: public ::testing::Test
    {
    public:
      int a;
      virtual void SetUp()
      {
        /**N ここにテスト実行前に呼び出したい処理を書く */
        a = 1;
      }
      virtual void TearDown()
      {
        /**N ここにテスト実行後に呼び出したい処理を書く */
        a = 0;
      }
    };
  2. テストフィクスチャを以下のように書く。 まずはSetUp?のテスト。
    TESTから TEST_Fになっていることに注意
    TEST_Fの第一引数は クラスと同じ名前を(addTest)
    TEST_Fの第二引数は テストを表す文を(CheckSetUp?)
    第二引数は英語でテストの内容がわかるように名付ける所がミソ
    /**N SetUpでちゃんと1がセットされているか チェック */
    TEST_F(addTest, CheckSetUp)
    {
      int num = a;
      EXPECT_EQ( 1, num);
    }
    [----------] Global test environment tear-down
    [==========] 2 tests from 2 test cases ran. (0 ms total)
    [  PASSED  ] 2 tests.
  3. 次は足し算のテスト
    TEST_F(addTest, AddTwice)
    {
      int num = add(1,2) + a;
      EXPECT_EQ( 4, num);
    }
    
    [----------] Global test environment tear-down
    [==========] 3 tests from 2 test cases ran. (0 ms total)
    [  PASSED  ] 3 tests.
  4. まぐれでテストが通過したかもしれないので、もう一つテストを追加
    テストは不安な数だけ書くのがテストファーストの原則。
    TEST_F(addTest, AddThenMainasu)
    {
      int num = add(1,2) - a;
      EXPECT_EQ( 2, num);
    }
    [----------] Global test environment tear-down
    [==========] 4 tests from 2 test cases ran. (0 ms total)
    [  PASSED  ] 4 tests.
    

GoogleTest?の結果をGUI表示

gtest-gbarをダウンロード http://code.google.com/p/gtest-gbar/

これであなたも 「レッド」→ 「グリーン」→ 「リファクタリング」