C++使用gtest框架編寫單元測試的教程詳解
前言
gtest 是 Google 開發(fā)的一個用于 C++ 的測試框架,廣泛應用于編寫和運行單元測試,并且支持任何類型的測試,而不僅僅是單元測試。
注意:
- 本教程使用 cmake 啟動并運行 GoogleTest:需提前安裝 CMake。
- 術語:測試(Test)、測試用例(Test Case)和測試套件(Test Suite)。
使用 cmake 啟動并運行 gtest
1. 設置項目
CMake 使用 CMakeLists.txt 來配置項目的構(gòu)建系統(tǒng)【使用該文件設置項目,并聲明對 gtest 的依賴】
首先,創(chuàng)建一個項目的目錄:
mkdir my_project && cd my_project
接下來,將創(chuàng)建 CMakeLists.txt 文件并聲明對 GoogleTest 的依賴。
在項目目錄(my_project)中,創(chuàng)建一個名為 CMakeLists.txt 的文件:
vim CMakeLists.txt
其內(nèi)容如下:
cmake_minimum_required(VERSION 3.14) project(my_project) # 設置 C++ 標準為 C++14 set(CMAKE_CXX_STANDARD 14) # 強制要求編譯器支持所選的 C++ 標準 set(CMAKE_CXX_STANDARD_REQUIRED ON) # 包含 FetchContent 模塊,用于從外部資源獲取依賴項 include(FetchContent) FetchContent_Declare( googletest URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip ) # 對于 Windows 系統(tǒng):防止覆蓋父項目的編譯器/鏈接器設置 set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # 使得 GoogleTest 可用 FetchContent_MakeAvailable(googletest)
這個文件中包括了以下部分:
- cmake_minimum_required(VERSION 3.14):指定了 CMake 的最低版本要求。
- project(my_project):定義了項目的名稱。
- set(CMAKE_CXX_STANDARD 14) 和 set(CMAKE_CXX_STANDARD_REQUIRED ON):設置了 C++ 標準為 C++14,且要求編譯器支持此標準。
- include(FetchContent):包含了 CMake 的 FetchContent 模塊,用于從外部資源(如 GitHub)獲取依賴項。
- FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip):聲明了對 GoogleTest 的依賴,指定了下載地址。
- set(gtest_force_shared_crt ON CACHE BOOL "" FORCE):對于 Windows 系統(tǒng),防止覆蓋父項目的編譯器/鏈接器設置。
- FetchContent_MakeAvailable(googletest):獲取并使 GoogleTest 可用。
2. 創(chuàng)建并運行二進制文件
將 gtest 聲明為一個依賴項后,你就可以在自己的項目中使用 GoogleTest 代碼。
舉例來說,在 my_project 目錄中創(chuàng)建一個名為 hello_test.cc 的文件:
vim hello_test.cc
內(nèi)容如下:
#include <gtest/gtest.h> // 展示一些基本斷言。 TEST(HelloTest, BasicAssertions) { // 期望兩個字符串不相等。 EXPECT_STRNE("hello", "world"); // 期望相等。 EXPECT_EQ(7 * 6, 42); }
要構(gòu)建代碼,需要將以下內(nèi)容添加到你的 CMakeLists.txt 文件末尾:
# 啟用測試 enable_testing() # 聲明要測試的可執(zhí)行文件 add_executable( hello_test hello_test.cc ) # 鏈接 GoogleTest 主要庫 target_link_libraries( hello_test GTest::gtest_main ) # 包含 GoogleTest 模塊 include(GoogleTest) # 使用 gtest_discover_tests 函數(shù)來自動發(fā)現(xiàn)并添加測試 gtest_discover_tests(hello_test)
上述配置啟用了 CMake 中的測試,聲明了要構(gòu)建的 C++ 測試二進制文件(hello_test),并將其鏈接到 GoogleTest(gtest_main)。
最后兩行啟用了 CMake 的測試運行器,使用 GoogleTest 的 CMake 模塊來發(fā)現(xiàn)包含在二進制文件中的測試。
現(xiàn)在你可以依據(jù)下面指令構(gòu)建和運行你的測試:
cmake -S . -B build
- 告訴 CMake 在當前目錄(
-S .
)中查找 CMakeLists.txt 文件,并在指定的構(gòu)建目錄build
中生成構(gòu)建系統(tǒng)文件(-B build
)。
cmake --build build
cmake
是調(diào)用 CMake 工具的命令。--build
是用于告訴 CMake 執(zhí)行構(gòu)建操作的選項。build
是構(gòu)建目錄的路徑,指定了 CMake 在build 路徑下執(zhí)行構(gòu)建操作。
cd build && ctest
- cd build 進入構(gòu)建目錄。
- ctest 會查找構(gòu)建目錄中的測試,并執(zhí)行它們。
顯示如下內(nèi)容:
恭喜!你成功地構(gòu)建并運行了一個使用 GoogleTest 的測試二進制文件。
gtest 入門
使用 gtest 時,首先要會編寫斷言(assertions),這些是檢查條件是否為真的語句。
一個斷言的結(jié)果可以是成功、非致命失敗或致命失敗【如果發(fā)生致命失敗,它會中止當前函數(shù);否則程序會正常繼續(xù)執(zhí)行】
測試使用斷言來驗證被測試代碼的行為。如果一個測試崩潰或有一個失敗的斷言,那么它失?。环駝t它成功。
- 一個測試套件(test suite)包含一個或多個測試(test)。應該將你的測試(test)分組到反映被測代碼結(jié)構(gòu)的測試套件(test suite)中。
- 一個測試程序可以包含多個測試套件(test suite)。
接下來,我們將解釋如何編寫一個測試程序,從單個斷言級別開始,逐步構(gòu)建到測試和測試套件。
1 斷言(assertions)
斷言(assertions)是類似函數(shù)調(diào)用的宏。你可以通過對其行為進行斷言來測試一個類或函數(shù)。當一個斷言失敗時,gtest 會打印斷言的源文件和行號位置,以及一個失敗消息。你還可以提供一個自定義的失敗消息,它將附加到 gtest 的消息中。
這些斷言成對出現(xiàn),測試相同的事物,但對當前函數(shù)有不同的影響。
- ASSERT_* 版本在失敗時會生成致命失敗,并中止當前函數(shù)。
- EXPECT_* 版本生成非致命失敗,不會中止當前函數(shù)。
通常情況下,優(yōu)先使用 EXPECT_*,因為它們允許在一個測試中報告多個失敗。然而,如果在相關斷言失敗時繼續(xù)執(zhí)行不合理,則應該使用 ASSERT_*。
由于失敗的 ASSERT_* 會立即返回當前函數(shù),可能會跳過其后的清理代碼,從而可能導致空間泄漏。根據(jù)泄漏的性質(zhì),如果除了斷言錯誤外還出現(xiàn)堆檢查器錯誤。
要提供自定義的失敗消息,只需使用 << 運算符或一系列此類運算符將其流式傳遞到宏中。
【示例】使用 ASSERT_EQ 和 EXPECT_EQ 宏來驗證值的相等性:
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length"; for (int i = 0; i < x.size(); ++i) { EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i; }
任何可以流式傳輸?shù)?ostream 的內(nèi)容都可以流式傳輸?shù)綌嘌院曛?- 特別是 C 字符串和字符串對象。如果將寬字符串(wchar_t*、 TCHAR*在 Windows 的UNICODE 模式下,或者 std::wstring)流式傳輸?shù)綌嘌灾?,則在打印時會被轉(zhuǎn)換為 UTF-8 編碼。
gtest 提供了一系列斷言,用于以各種方式驗證代碼的行為??梢詸z查布爾條件,基于關系運算符比較值,驗證字符串值、浮點值等等。甚至還有一些斷言可以通過提供自定義謂詞來驗證更復雜的狀態(tài)。
2 簡單測試
- 使用 TEST() 宏來定義和命名一個測試函數(shù)。這些是普通的 C++ 函數(shù),不返回任何值。
- 在這個函數(shù)中,除了你想包含的有效的 C++ 語句,使用各種 gtest 斷言來檢查值。
- 測試結(jié)果由斷言確定;如果測試中的任何斷言失?。o論是致命還是非致命),或者測試崩潰,整個測試都將失敗。否則,它成功。
TEST(TestSuiteName, TestName) { ... test body ... }
TEST() 宏的第一個參數(shù)是測試套件(test suite)的名稱,第二個參數(shù)是測試套件內(nèi)的測試名稱。兩個名稱都必須是有效的 C++ 標識符,并且不能包含下劃線【測試的全名=其所屬的測試套件+其單獨的名稱組成。來自不同測試套件的測試可以有相同的單獨名稱】
【示例】以一個簡單的整數(shù)函數(shù)為例
int Factorial(int n); // 返回 n 的階乘
此函數(shù)的測試套件可能如下:
// 測試 0 的階乘 TEST(FactorialTest, HandlesZeroInput) { // 期望 Factorial(0) 的結(jié)果是 1 EXPECT_EQ(Factorial(0), 1); } // 測試正數(shù)的階乘 TEST(FactorialTest, HandlesPositiveInput) { // 期望 Factorial(1) 的結(jié)果是 1 EXPECT_EQ(Factorial(1), 1); // 期望 Factorial(2) 的結(jié)果是 2 EXPECT_EQ(Factorial(2), 2); // 期望 Factorial(3) 的結(jié)果是 6 EXPECT_EQ(Factorial(3), 6); // 期望 Factorial(8) 的結(jié)果是 40320 EXPECT_EQ(Factorial(8), 40320); }
GoogleTest 按測試套件分組測試結(jié)果,因此邏輯上相關的測試應在同一個測試套件中;換句話說,它們的 TEST() 的第一個參數(shù)應該相同。
在上面的示例中,我們有兩個測試,HandlesZeroInput 和 HandlesPositiveInput,它們屬于同一個測試套件 FactorialTest。
在命名你的測試套件和測試時,應該遵循與命名函數(shù)和類相同的約定。
3 測試夾具:為多個測試使用相同的數(shù)據(jù)配置
Test Fixture(測試夾具)是指在測試運行前后,需要被執(zhí)行的代碼片段。
如果你發(fā)現(xiàn)自己在編寫兩個或更多操作相似數(shù)據(jù)的測試,可以使用測試夾具。這樣可以為多個不同的測試重復使用相同的對象配置。
創(chuàng)建夾具的步驟:
- 從 testing::Test 派生一個類。在類體開始處使用 protected:,因為我們希望從子類訪問夾具成員。
- 在類中聲明你需要使用的任何對象。
- 如果需要,編寫一個默認構(gòu)造函數(shù)或 SetUp() 函數(shù),為每個測試準備對象。
一個常見的錯誤是將 SetUp() 拼寫為小寫的 Setup() - 在 C++11 中使用 override 確保拼寫正確。- 如果需要,編寫一個析構(gòu)函數(shù)或 TearDown() 函數(shù)來釋放你在 SetUp() 中分配的任何資源。
- 如果需要,為你的測試定義共享的子程序。
// 定義夾具類 class MyTestFixture : public testing::Test { protected: // 在這里聲明你的對象 int* myObject; // 如果需要,編寫構(gòu)造函數(shù)或 SetUp() 函數(shù) void SetUp() override { myObject = new int(42); // 示例初始化 } // 如果需要,編寫析構(gòu)函數(shù)或 TearDown() 函數(shù) void TearDown() override { delete myObject; } }; // 使用 TEST_F() 進行測試 TEST_F(MyTestFixture, Test1) { // 可以在這里訪問 myObject EXPECT_EQ(*myObject, 42); } TEST_F(MyTestFixture, Test2) { // 也可以在這里訪問 myObject EXPECT_NE(*myObject, 0); }
使用夾具時,使用 TEST_F()
而不是 TEST()
,因為它允許你訪問測試夾具中的對象和子程序:
TEST_F(TestFixtureClassName, TestName) { ... test body ... }
到此這篇關于C++使用gtest框架編寫單元測試的教程詳解的文章就介紹到這了,更多相關C++ gtest單元測試內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
VsCode安裝和配置c/c++環(huán)境小白教程(圖文)
本文主要介紹了VsCode安裝和配置c/c++環(huán)境小白教程,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01C++?JSON庫?nlohmann::basic_json::array?的用法示例詳解
nlohmann::json是一個C++的JSON庫,它提供了一種容易和直觀的方法來處理JSON數(shù)據(jù),nlohmann::json::array()是用來創(chuàng)建一個JSON數(shù)組的方法,這篇文章主要介紹了C++ JSON庫nlohmann::basic_json::array的用法,需要的朋友可以參考下2023-06-06