GoogleTest導入記録

## 目的

1. 作成したC++プログラムのテストを行う
1. GoogleTestの練習
1. 共有ライブラリを使う練習

## 環境

- M1 MacBook Air Sonoma 14.7.1
- Homebrew 4.4.13
- GCC 14.2.0_1
- cmake 3.31.3
- GoogleTest stable 1.15.2

## 手順

### GoogleTestのinstall

Homebrewでinstallする。GitHubからクローンしてcmakeでビルドする方法もあり。

```shell
$ brew install googletest
```

installされたか確認。

```shell
$ ls -l /opt/homebrew/include | grep googletest
lrwxr-xr-x   1 user  admin    41 Dec 30 12:32 gmock -> ../Cellar/googletest/1.15.2/include/gmock
lrwxr-xr-x   1 user  admin    46 Dec 30 12:32 googlemock -> ../Cellar/googletest/1.15.2/include/googlemock
lrwxr-xr-x   1 user  admin    46 Dec 30 12:32 googletest -> ../Cellar/googletest/1.15.2/include/googletest
lrwxr-xr-x   1 user  admin    41 Dec 30 12:32 gtest -> ../Cellar/googletest/1.15.2/include/gtest
```

https://formulae.brew.sh/formula/googletest

## 基本的なTestの実行方法

1. **TEST()** マクロを使って、テスト関数の定義と命名を行う
1. テスト関数の中で、任意の有効なC++ステートメントをincludeし、GoogleTestのアサーションで動作を確認する
1. テスト結果は、アサーションによって決まる。どれか一つでもアサーションが失敗すると、テスト全体が止まる

**TEST()** マクロの基本的な使い方は、以下。

```c++
TEST(TestSuiteName, TestName) {
  ... test body ...
}
```

TestSuiteName: あるテストのまとまりの名前
TestName: そのテストの名前

GoogleTestは、関連するテストスイートごとにグループ化するため、関連するテストは同じテストスイートにする。使用可能アサーションは、下のリンクに一覧されている。

https://google.github.io/googletest/reference/assertions.html

### GoogleTest アサーションまとめ (引数と使用例付き)

| アサーション | 説明 | 引数 | 使用例 | 便利な場面 |
|---|---|---|---|---|
| `EXPECT_TRUE` | 条件が真であることを検証する。 | `condition`: 真偽値を返す式 | `EXPECT_TRUE(x > 5);` | 条件式の評価結果がテストの成否を決定する場合。 |
| `EXPECT_FALSE` | 条件が偽であることを検証する。 | `condition`: 真偽値を返す式 | `EXPECT_FALSE(y == 0);` | 条件式の評価結果がテストの成否を決定する場合。 |
| `EXPECT_EQ` | 2つの値が等しいことを検証する。 | `val1`, `val2`: 比較する2つの値 | `EXPECT_EQ(actual, expected);` | 変数の値が期待値と等しいかどうかを比較する場合。 |
| `EXPECT_NE` | 2つの値が等しくないことを検証する。 | `val1`, `val2`: 比較する2つの値 | `EXPECT_NE(result, error_code);` | 変数の値が特定のエラーコードと異なるかどうかを比較する場合。 |
| `EXPECT_LT` | ある値が別の値より小さいことを検証する。 | `val1`, `val2`: 比較する2つの値 | `EXPECT_LT(a, b);` | 変数の大小関係（より小さい）を比較する場合。 |
| `EXPECT_LE` | ある値が別の値以下であることを検証する。 | `val1`, `val2`: 比較する2つの値 | `EXPECT_LE(count, max_count);` | 変数の大小関係（以下）を比較する場合。 |
| `EXPECT_GT` | ある値が別の値より大きいことを検証する。 | `val1`, `val2`: 比較する2つの値 | `EXPECT_GT(size, 0);` | 変数の大小関係（より大きい）を比較する場合。 |
| `EXPECT_GE` | ある値が別の値以上であることを検証する。 | `val1`, `val2`: 比較する2つの値 | `EXPECT_GE(age, 18);` | 変数の大小関係（以上）を比較する場合。 |
| `EXPECT_STREQ` | 2つのC文字列の内容が同じであることを検証する。 | `str1`, `str2`: 比較するC文字列 | `EXPECT_STREQ("hello", actual_string);` | C文字列の内容を比較する場合。 |
| `EXPECT_STRNE` | 2つのC文字列の内容が異なることを検証する。 | `str1`, `str2`: 比較するC文字列 | `EXPECT_STRNE("goodbye", input);` | C文字列の内容が特定の値と異なるかどうかを比較する場合。 |

**補足:**

*   `ASSERT_` で始まるアサーションは、失敗した場合にテスト関数を即座に中断する。`EXPECT_` で始まるアサーションは、失敗してもテスト関数は続行される
*   これらのアサーションは、単体テストにおいて、コードの挙動が期待通りかどうかを検証するために使用される
*   引数の型が一致しない場合、コンパイルエラーが発生する
*   浮動小数点数の比較には、`EXPECT_FLOAT_EQ`、`EXPECT_DOUBLE_EQ`、`EXPECT_NEAR` など、より適切なアサーションが用意されている。これは、浮動小数点数の演算誤差を考慮するため


## GoogleTestの公式サンプルを試す

https://github.com/google/googletest/tree/main/googletest/samples

このリンクにSampleが乗っていたので、試しに実行する。下のコードは、Googleのサンプルコードを一部改変したものである。BSDライセンスなので、本記事への記載は問題ないと思われる。オリジナルテストコードの著作権は、Google Inc.に帰属する。

**sample1.hpp**

```c++
#pragma once

// 階乗の計算
int Factorial(int n);

// 素数判定
bool IsPrime(int n);
```

**sample1.cpp**

```c++
#include "sample1.hpp"

// 階乗の計算
int Factorial(int n)
{
    int result = 1;
    for (int i = 1; i <= n; i++)
    {
        result *= i;
    }
    return result;
}

// 素数判定
bool IsPrime(int n)
{
    // 1を除外
    if (n <= 1)
        return false;

    // 偶数のうち2だけ素数
    if (n % 2 == 0)
        return n == 2;

    // 3以降
    for (int i = 3;; i += 2)
    {
        // nの平方根だけの回数実行
        if (i > n / i)
            break;

        // 合致する値があれば、素数でない
        if (n % i == 0)
            return false;
    }

    return true;
}

```

階乗の計算と素数判定のシンプルなプログラム。次に、このプログラムに対するテストコードを作成する。(TDDがお好きな方は、こちらから先に書いても良いかもしれない)。

```c++
#include "sample1.hpp"
#include <limits.h>

#include <gtest/gtest.h>

TEST(FactorialTest, Negative)
{
    // This test is named "Negative", and belongs to the "FactorialTest"
    // test case.
    EXPECT_EQ(1, Factorial(-5));
    EXPECT_EQ(1, Factorial(-1));
    EXPECT_GT(Factorial(-10), 0);
}

// Tests factorial of 0.
TEST(FactorialTest, Zero) { EXPECT_EQ(1, Factorial(0)); }

// Tests factorial of positive numbers.
TEST(FactorialTest, Positive)
{
    EXPECT_EQ(1, Factorial(1));
    EXPECT_EQ(2, Factorial(2));
    EXPECT_EQ(6, Factorial(3));
    EXPECT_EQ(40320, Factorial(8));
}

// Tests IsPrime()

// Tests negative input.
TEST(IsPrimeTest, Negative)
{
    // This test belongs to the IsPrimeTest test case.

    EXPECT_FALSE(IsPrime(-1));
    EXPECT_FALSE(IsPrime(-2));
    EXPECT_FALSE(IsPrime(INT_MIN));
}

// Tests some trivial cases.
TEST(IsPrimeTest, Trivial)
{
    EXPECT_FALSE(IsPrime(0));
    EXPECT_FALSE(IsPrime(1));
    EXPECT_TRUE(IsPrime(2));
    EXPECT_TRUE(IsPrime(3));
}

// Tests positive input.
TEST(IsPrimeTest, Positive)
{
    EXPECT_FALSE(IsPrime(4));
    EXPECT_TRUE(IsPrime(5));
    EXPECT_FALSE(IsPrime(6));
    EXPECT_TRUE(IsPrime(23));
}
```

**main.cpp**

```c++
#include <gtest/gtest.h>
#include "sample1.hpp"

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
```

このmain関数で、テストを実行する。最終的なファイル構成は、以下のようになった。

```
├── CMakeLists.txt
├── main.cpp
├── sample1.cpp
├── sample1.hpp
└── sample1_unittest.cpp
``` 

### CMakeLists.txtの作成

```cmake
# CMakeのバージョン設定
cmake_minimum_required(VERSION 3.13)

# C++のバージョン設定
set(CMAKE_CXX_STANDARD 14)

# プロジェクト名
project(gtest_practice)

# googletestをひもづける
find_package(GTest REQUIRED)

# テスト用の実行ファイルを作成
add_executable(
    ${PROJECT_NAME}_tests
    main.cpp
    sample1.cpp
    sample1_unittest.cpp
)

# googletestをリンク
target_link_libraries(
    ${PROJECT_NAME}_tests
    PRIVATE
    GTest::gtest
)
```



```shell
cmake -S . -B build
cmake --build build
```

## 結果

```shell
$ ./gtest_practice_tests 
[==========] Running 6 tests from 2 test suites.
[----------] Global test environment set-up.
[----------] 3 tests from FactorialTest
[ RUN      ] FactorialTest.Negative
[       OK ] FactorialTest.Negative (0 ms)
[ RUN      ] FactorialTest.Zero
[       OK ] FactorialTest.Zero (0 ms)
[ RUN      ] FactorialTest.Positive
[       OK ] FactorialTest.Positive (0 ms)
[----------] 3 tests from FactorialTest (0 ms total)

[----------] 3 tests from IsPrimeTest
[ RUN      ] IsPrimeTest.Negative
[       OK ] IsPrimeTest.Negative (0 ms)
[ RUN      ] IsPrimeTest.Trivial
[       OK ] IsPrimeTest.Trivial (0 ms)
[ RUN      ] IsPrimeTest.Positive
[       OK ] IsPrimeTest.Positive (0 ms)
[----------] 3 tests from IsPrimeTest (0 ms total)

[----------] Global test environment tear-down
[==========] 6 tests from 2 test suites ran. (0 ms total)
[  PASSED  ] 6 tests.
```

## その他の調べ物

### C言語標準ライブラリ・limits.h

```c++
#include <limits.h>
```

>ヘッダー<limits.h> では、標準的な整数型のさまざまな限界値やパラメータに展開するマクロがいくつか定義されています。標準的な整数型のさまざまな制限やパラメータに展開するいくつかのマクロを定義しています。
フリースタンディング環境で使える標準ライブラリの1つです。

https://ja.wikibooks.org/wiki/C%E8%A8%80%E8%AA%9E/%E6%A8%99%E6%BA%96%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA/limits.h

色々な変数の限界値や大きさをまとめたマクロ。標準ライブラリの一つ。

## 参考

https://github.com/google/googletest

https://github.com/google/googletest/tree/main/googletest/samples

https://google.github.io/googletest/

https://google.github.io/googletest/reference/assertions.html

https://ja.wikibooks.org/wiki/C%E8%A8%80%E8%AA%9E/%E6%A8%99%E6%BA%96%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA/limits.h

https://qiita.com/utkamioka/items/cacb1001bd2abf605b15

https://zenn.dev/rjkuro/articles/054dab5b0e4f40

https://github.com/google/googletest/blob/main/googletest/samples/sample1.h

https://github.com/google/googletest/blob/main/googletest/samples/sample1.cc