GoogleTestの書き方(初心者向け)

※この記事は https://kunosu.qrunch.io/entries/aHyZkfilUi1Ek9UL に移動しました。

---

GoogleTestの公式チートシート→ http://opencv.jp/googlemockdocs/cheatsheet.html

- こんな人向け
    - 仕事でGoogleTestを使うことになったが使ったことがない
    - GoogleTest自体の導入はすでに済んでいる

## テストコードの全体像

GoogleTestはC++で動く。
Cで実装したコードでもCの部分を「`extern "C"`」で囲めば試験可能。

全体はこんな感じ

1. インクルード(試験対象が動くのに必要なヘッダファイルもインクルードする)
1. モッククラスの宣言
1. テストフィクスチャクラス
1. テストケース1
1. テストケース2
1. ...
1. テストケースn

今回は以下の関数に対するテストコードを紹介します。

```c
int func(int a, b) {
    if (0 < sub_func(a)) {
        return a + b;
    } else {
        return a - b;
    }
}
```

ここで、サブ関数`sub_func()`はまだ作られていません。
ただし、I/Oは決まっています。

```c
// プロトタイプ宣言
int sub_func(int a);
```

また、組込みだと開発対象の機械専用の関数(メーカーが独自に用意した関数など)もあります。

## モッククラスの宣言

まずはサブ関数の代わりに動くモックを作成します。

```cpp
// モッククラスの宣言
class TestMock {
public:
    //MOCK_METHODn(関数名, 戻り値の型(引数1, 引数2, ...))
    //nは引数の数
    MOCK_METHOD1(sub_func, int(int a));
} testMock;

//モック化したい関数を書く
int sub_func(int a) {
    return testMock->sub_func(a);
}
```

## テストフィクスチャクラスの定義

ここでは試験で使うクラス、及び共有リソース(配列など)の定義、初期化を行う。

> - テストフィクスチャ
>     - テストを実行させるために必要な状態や前提条件
>     - 参考 : https://www.weblio.jp/content/テストフィクスチャ
> - fixture(フィクスチャ) : 備品

毎回動作が決まっているモックもここで動作を定義する。

```cpp
// テストフィクスチャクラスの定義
class FuncTest : public Test {
    protected:
        // 試験開始時に一回だけ実行
        static void SetUpTestCase() {
        }

        // 試験終了時に一回だけ実行
        static void TearDownTestCase() {
        }

        // 各テストケース実行前に実行
        virtual void SetUp() {
        }

        // 各テストケース実行後に実行
        virtual void SetUp() {
        }

        // 試験対象の引数などを宣言
        int a;
        int b;
        int ans;
};
```

## テストケース

ここで一つ一つのテストケースを記述。

```cpp
//TEST_F(テストフィクスチャクラス名, テストケース名)
TEST_F(FuncTest, sample1) {
    //パラメータを設定
    a = 1;
    b = 2;

    //sub_func呼び出し時、常に0を返すように設定
    EXPECT_CALL(*testMock, sub_func(_)).WillRepeatedly(Return(0));

    // 試験対象呼び出し
    ans = func(a, b);

    // 戻り値が期待するものか確認
    EXPECT_EQ(3, ans);
}
```

### モックの動作を指定

`EXPECT_CALL()` 関数で指定する。
引数は2つで、1つ目がモックを作成したときのモッククラス、2つ目で実際に呼び出される関数名とその引数を指定する。
例のようにアンダーバーにすると任意の値になる。

```cpp
EXPECT_CALL(*testMock, sub_func(_)).WillRepeatedly(Return(0));
```

戻り値は `WillRepeatedly(Return(...))` の `Return()` の引数として記載。

#### 呼び出し回数の指定

引数、戻り値の指定だけでなく、`.Time(1)` を追加して呼び出し回数も設定可能。
「この関数は呼び出されないことを試験したい」場合は呼び出し回数0にすればOK。

```cpp
// 3回呼び出されることを期待
EXPECT_CALL(*testMock, sub_func(_)).Times(3).WillRepeatedly(Return(0));
```

#### 呼び出されるたびに異なる値を返す

- `WillRepeatedly(Return(x))`: 呼び出されるたびに `x` を返す
- `WillOnce(Return(x))` :1回だけ `x` を返す

```cpp
// 1回目は0、2回目は1、3回目は2を返す
EXPECT_CALL(*testMock, sub_func(_)).WillOnce(Return(0)).WillOnce(Return(1)).WillOnce(Return(2));
```

#### スタブを動かす
同じテストコード内にスタブを作成。

```cpp
int stb_sub_func(int a) {
  // テストで動かすのに十分な処理を記載(引数チェックとか)
}
```

`Invoke` でスタブを指定するとサブ関数呼び出し時にスタブを代わりに実行。

```cpp
// stb_sub_func()を実行
EXPECT_CALL(*testMock, sub_func(_)).WillRepeatedly(Invoke(stb_sub_func));
```

### 戻り値などを比較

成功条件(`flg` の値) | コード
:-------------------|:---------------------
true                | `EXPECT_TRUE(flg);`
false               | `EXPECT_FALSE(flg);`

2つの値を比較する

成功条件 | コード
:------:|:-------------------
a == b  | `EXPECT_EQ(a, b);`
a != b  | `EXPECT_NE(a, b);`
a < b   | `EXPECT_LT(a, b);`
a <= b  | `EXPECT_LE(a, b);`
a > b   | `EXPECT_GT(a, b);`
a => b  | `EXPECT_GE(a, b);`