---
# System prepended metadata

title: Google Test Tutorial

---

# Google Test Tutorial
## **為什麼用 Google Test (gtest)**
Google Test (gtest) 是一個由 Google 開發的 C++ 單元測試框架，用於檢查程式的功能是否正常。它可以幫助我們快速檢查專案或程式內的C/C++函式是否正確執行。

## gtest程式介紹
以下為一個最基本的範例程式檔設計
:::spoiler example.cpp
```Cpp=
//example.cpp
#include <gtest/gtest.h>

// 測試的函式（可以從其他檔案中引入）
int add(int a, int b){
    return a+b;
}


//測試案例一
TEST(testCase, test1){
    EXPECT_EQ(myadd(1, 2), 3);
}

//測試案例二
TEST(testCase, test2){
    EXPECT_EQ(myadd(1, -1), 0);
}

// 主函式：執行所有測試案例
int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
```
:::
### 解說
1. 被檢測的函式
    - 可以直接在gtest檔案裡宣告，也可以引入其他檔案的函式
        - 引入其他檔案的函式，舉例：
            #### example.h
            ```h=
            #pragma once
            int add(int a, int b);
            ```

            #### example.c
            ```C=
            #include "example.h"

            int add(int a, int b) {
                return a + b;
            }
            ```

            #### gtest.cpp
            ```Cpp=
            #include <gtest/gtest.h>
            extern "C" {
                #include "example.h" // 引入要測試的函式
            }

            TEST(AdditionTest, PositiveNumbers) {
                EXPECT_EQ(add(2, 3), 5);
            }
            //接主程式
            ```
2. 測試函式
    ```Cpp=
    Test(類別,測試名){
        //你的程式碼
    }
    ```
    - 類別和測試名的設置在測試時會顯示出來
        - 如：
            - 加法函數測試一,正數加減
            - 加法函數測試二,負數加減
            - 乘法函數測試一,100內相乘
            - 前為類別，後為測試名
    - 程式碼區域則需使用斷言函式，常見的斷言函式有：
    ### **值比較**
| 函式                  | 描述                                      | 範例                              |
|-----------------------|-------------------------------------------|-----------------------------------|
| `EXPECT_EQ(val1, val2)` | 檢查兩個值是否相等                       | `EXPECT_EQ(3, add(1, 2));`       |
| `EXPECT_NE(val1, val2)` | 檢查兩個值是否不相等                     | `EXPECT_NE(3, subtract(1, 2));`  |
| `EXPECT_LT(val1, val2)` | 檢查 `val1` 是否小於 `val2`              | `EXPECT_LT(2, 5);`               |
| `EXPECT_LE(val1, val2)` | 檢查 `val1` 是否小於等於 `val2`          | `EXPECT_LE(5, 5);`               |
| `EXPECT_GT(val1, val2)` | 檢查 `val1` 是否大於 `val2`              | `EXPECT_GT(5, 2);`               |
| `EXPECT_GE(val1, val2)` | 檢查 `val1` 是否大於等於 `val2`          | `EXPECT_GE(5, 5);`               |

### **布林值檢查**
| 函式                     | 描述                           | 範例                          |
|--------------------------|--------------------------------|-------------------------------|
| `EXPECT_TRUE(condition)` | 檢查條件是否為 `true`          | `EXPECT_TRUE(isPositive(5));` |
| `EXPECT_FALSE(condition)`| 檢查條件是否為 `false`         | `EXPECT_FALSE(isNegative(5));`|

---

### **浮點數檢查**
用於比較浮點數（解決精度問題）。
| 函式                           | 描述                                      | 範例                                     |
|--------------------------------|-------------------------------------------|------------------------------------------|
| `EXPECT_FLOAT_EQ(val1, val2)`  | 檢查兩個浮點數是否相等（以小數精度比較）  | `EXPECT_FLOAT_EQ(0.1 + 0.2, 0.3);`      |
| `EXPECT_NEAR(val1, val2, abs_error)` | 檢查兩個值是否在允許誤差範圍內          | `EXPECT_NEAR(3.14, computePi(), 0.01);` |

### **字串比較**
| 函式                              | 描述                                     | 範例                                        |
|-----------------------------------|------------------------------------------|---------------------------------------------|
| `EXPECT_STREQ(str1, str2)`        | 檢查兩個 C 字符串是否相等               | `EXPECT_STREQ("hello", getGreeting());`     |
| `EXPECT_STRNE(str1, str2)`        | 檢查兩個 C 字符串是否不相等             | `EXPECT_STRNE("hello", "world");`           |
| `EXPECT_STRCASEEQ(str1, str2)`    | 檢查兩個 C 字符串是否相等（忽略大小寫） | `EXPECT_STRCASEEQ("hello", "HELLO");`       |
| `EXPECT_STRCASENE(str1, str2)`    | 檢查兩個 C 字符串是否不相等（忽略大小寫）| `EXPECT_STRCASENE("hello", "WORLD");`       |

---

### 即時終止測試的斷言

**`ASSERT_*` 系列** 與 `EXPECT_*` 類似，但當斷言失敗時會立即終止該測試案例，而不執行後續測試。

| 函式                  | 描述                                      | 範例                              |
|-----------------------|-------------------------------------------|-----------------------------------|
| `ASSERT_EQ(val1, val2)` | 檢查兩個值是否相等                       | `ASSERT_EQ(3, add(1, 2));`       |
| `ASSERT_NE(val1, val2)` | 檢查兩個值是否不相等                     | `ASSERT_NE(3, subtract(1, 2));`  |
| `ASSERT_TRUE(condition)`| 檢查條件是否為 `true`                    | `ASSERT_TRUE(isValid(value));`   |
| `ASSERT_FALSE(condition)`| 檢查條件是否為 `false`                  | `ASSERT_FALSE(isNegative(5));`  |
---

3. 主程式
一般情況下利用其配套好的程式就好，會執行所有測試案例

```Cpp=
// 主函式：執行所有測試案例
int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

```

## 設計自己的gtest
1. 確認是否引用外部函式
2. 新增想要測試的斷言函式
3. 主程式帶入，執行所有測試案例


## 利用Cmake執行gtest
### cmake安裝
確認自己是否安裝cmake
```bash
sudo apt-get install cmake
cmake --version
```
### 建立專案資料夾或進入被測試檔案的專案資料夾
```bash
mkdir my_project && cd my_project
```

### 當前資料夾路徑預覽
我們的資料夾等等應該長這樣
```
my_project
├── CMakeLists.txt
├── example.c
├── example.h
└── my_gtest.cpp
```

### 在被測試檔案同層資料夾創立CMakeLists.txt
這邊是固定的，不須修改，此為從github中下載gtest
```txt
cmake_minimum_required(VERSION 3.14)
project(my_project)

# GoogleTest requires at least C++14
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)
FetchContent_Declare(
  googletest
  URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
```

### 建立/引用相關檔案
以下為範例檔，需換成自己的檔案
:::spoiler example.c
```C=
#include "example.h"

int myadd(int a, int b) {
    return a + b;
}
```
:::

:::spoiler example.h
```h=
#pragma once
int myadd(int a, int b);
```
:::

:::spoiler my_gtest.cpp
```Cpp=
#include <gtest/gtest.h>
extern "C" {
    #include "example.h" // 引入要測試的函式
}
TEST(testCase, test1){
    EXPECT_EQ(myadd(1, 2), 3);
}

//測試案例二
TEST(testCase, test2){
    EXPECT_EQ(myadd(1, -1), 0);
}

// 主函式：執行所有測試案例
int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
```
:::

### Cmake配置Google Test 測試執行環境，需加在CMakeLists.txt末尾
```txt
enable_testing() //啟用 CMake 的測試功能

add_library(myadd example.c) //將被測試檔編譯成函式庫，供多個檔案使用，這邊自訂為myadd
add_executable(    //將測試程式編譯為一個可執行檔案
  my_gtest         //測試檔的執行檔(換成自己的)
  my_gtest.cpp     //測試檔原檔(換成自己的)

)
target_link_libraries(    //將 Google Test 等函式庫鏈接到可執行檔 my_gtest
  my_gtest    //可執行檔(換成自己的)
  myadd       //自訂函式庫(換成自己的)
  GTest::gtest_main
)

include(GoogleTest)    //引入 Google Test 的 CMake 模組
gtest_discover_tests(my_gtest)    //自動找尋my_gtest(換成自己的)中的gtest測試案例，註冊到ctest框架中
```

### 下指令執行

#### 在被測試函式檔同層資料夾下指令
```bash=
cmake -S . -B build    //配置專案，會自行創立build資料夾，不須自行新增
cmake --build build    //編譯專案
cd build && ctest      //執行測試
```
### 此時資料夾預覽
```
my_project
├── build
├── CMakeLists.txt
├── example.c
├── example.h
└── my_gtest.cc
```
執行完上述指令就成功完成了自己的gtest啦!!
![image](https://hackmd.io/_uploads/rk7846tHJg.png)
