# 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)