人人妻人人澡人人爽人人精品av_精品乱码一区内射人妻无码_老司机午夜福利视频_精品成品国色天香摄像头_99精品福利国产在线导航_野花社区在线观看视频_大地资源在线影视播放_东北高大肥胖丰满熟女_金门瓶马车内剧烈运动

首頁>國內 > 正文

使用 GoogleTest 和 CTest 進行單元測試

2023-08-02 14:21:54來源:Linux中國

進行單元測試可以提高代碼質量,并且它不會打斷你的工作流。

本文是使用 CMake 和 VSCodium 設置一個構建系統(tǒng)的后續(xù)文章。

在上一篇文章中我介紹了基于VSCodium和CMake配置構建系統(tǒng)。本文我將介紹如何通過GoogleTest和CTest將單元測試集成到這個構建系統(tǒng)中。


【資料圖】

首先克隆這個倉庫,用 VSCodium 打開,切換到devops_2標簽。你可以通過點擊main分支符號(紅框處),然后選擇devops_2標簽(黃框處)來進行切換:

VSCodium tag

或者你可以通過命令行來切換:

$ git checkout tags/devops_2
GoogleTest

GoogleTest 是一個平臺無關的開源 C++ 測試框架。單元測試是用來驗證單個邏輯單元的行為的。盡管 GoogleTest 并不是專門用于單元測試的,我將用它對Generator庫進行單元測試。

在 GoogleTest 中,測試用例是通過斷言宏來定義的。斷言可能產生以下結果:

成功: 測試通過。非致命失敗: 測試失敗,但測試繼續(xù)。致命失敗: 測試失敗,且測試終止。

致命斷言和非致命斷言通過不同的宏來區(qū)分:

ASSERT_*: 致命斷言,失敗時終止。EXPECT_*: 非致命斷言,失敗時不終止。

谷歌推薦使用EXPECT_*宏,因為當測試中包含多個的斷言時,它允許繼續(xù)執(zhí)行。斷言有兩個參數(shù):第一個參數(shù)是測試分組的名稱,第二個參數(shù)是測試自己的名稱。Generator只定義了generate(...)函數(shù),所以本文中所有的測試都屬于同一個測試組:GeneratorTest。

針對generate(...)函數(shù)的測試可以從GeneratorTest.cpp中找到。

引用一致性檢查

generate(...)函數(shù)有一個std::stringstream的引用作為輸入參數(shù),并且它也將這個引用作為返回值。第一個測試就是檢查輸入的引用和返回的引用是否一致。

TEST(GeneratorTest, ReferenceCheck){    const int NumberOfElements = 10;    std::stringstream buffer;    EXPECT_EQ(        std::addressof(buffer),        std::addressof(Generator::generate(buffer, NumberOfElements))    );}

在這個測試中我使用std::addressof來獲取對象的地址,并用EXPECT_EQ來比較輸入對象和返回對象是否是同一個。

檢查元素個數(shù)

本測試檢查作為輸入的std::stringstream引用中的元素個數(shù)與輸入參數(shù)中指定的個數(shù)是否相同。

TEST(GeneratorTest, NumberOfElements){    const int NumberOfElements = 50;    int nCalcNoElements = 0;    std::stringstream buffer;    Generator::generate(buffer, NumberOfElements);    std::string s_no;    while(std::getline(buffer, s_no, " ")) {        nCalcNoElements++;    }    EXPECT_EQ(nCalcNoElements, NumberOfElements);}
亂序重排

本測試檢查隨機化引擎是否工作正常。如果連續(xù)調用兩次generate函數(shù),應該得到的是兩個不同的結果。

TEST(GeneratorTest, Shuffle){    const int NumberOfElements = 50;    std::stringstream buffer_A;    std::stringstream buffer_B;    Generator::generate(buffer_A, NumberOfElements);    Generator::generate(buffer_B, NumberOfElements);    EXPECT_NE(buffer_A.str(), buffer_B.str());}
求和校驗

與前面的測試相比,這是一個大體量的測試。它檢查 1 到 n 的數(shù)值序列的和與亂序重排后的序列的和是否相等。generate(...)函數(shù)應該生成一個 1 到 n 的亂序的序列,這個序列的和應當是不變的。

TEST(GeneratorTest, CheckSum){    const int NumberOfElements = 50;    int nChecksum_in = 0;    int nChecksum_out = 0;    std::vector vNumbersRef(NumberOfElements); // Input vector    std::iota(vNumbersRef.begin(), vNumbersRef.end(), 1); // Populate vector    // Calculate reference checksum    for(const int n : vNumbersRef){        nChecksum_in += n;    }    std::stringstream buffer;    Generator::generate(buffer, NumberOfElements);    std::vector vNumbersGen; // Output vector    std::string s_no;    // Read the buffer back back to the output vector    while(std::getline(buffer, s_no, " ")) {        vNumbersGen.push_back(std::stoi(s_no));    }    // Calculate output checksum    for(const int n : vNumbersGen){        nChecksum_out += n;    }    EXPECT_EQ(nChecksum_in, nChecksum_out);}

你可以像對一般 C++ 程序一樣調試這些測試。

CTest

除了嵌入到代碼中的測試之外,CTest提供了可執(zhí)行程序的測試方式。簡而言之就是通過給可執(zhí)行程序傳入特定的參數(shù),然后用正則表達式對它的輸出進行匹配檢查。通過這種方式可以很容易檢查程序對于不正確的命令行參數(shù)的反應。這些測試定義在頂層的CMakeLists.txt文件中。下面我詳細介紹 3 個測試用例:

參數(shù)正常

如果輸入參數(shù)是一個正整數(shù),程序應該輸出應該是一個數(shù)列:

add_test(NAME RegularUsage COMMAND Producer 10)set_tests_properties(RegularUsage    PROPERTIES PASS_REGULAR_EXPRESSION "^[0-9 ]+")
沒有提供參數(shù)

如果沒有傳入參數(shù),程序應該立即退出并提示錯誤原因:

add_test(NAME NoArg COMMAND Producer)set_tests_properties(NoArg    PROPERTIES PASS_REGULAR_EXPRESSION "^Enter the number of elements as argument")
參數(shù)錯誤

當傳入的參數(shù)不是整數(shù)時,程序應該退出并報錯。比如給Producer傳入參數(shù)ABC

add_test(NAME WrongArg COMMAND Producer ABC)set_tests_properties(WrongArg    PROPERTIES PASS_REGULAR_EXPRESSION "^Error: Cannot parse")
執(zhí)行測試

可以使用ctest -R Usage -VV命令來執(zhí)行測試。這里給ctest的命令行參數(shù):

-R <測試名稱>: 執(zhí)行單個測試-VV:打印詳細輸出

測試執(zhí)行結果如下:

$ ctest -R Usage -VVUpdatecTest Configuration from :/home/stephan/Documents/cpp_testing sample/build/DartConfiguration.tclUpdateCTestConfiguration from :/home/stephan/Documents/cpp_testing sample/build/DartConfiguration.tclTest project /home/stephan/Documents/cpp_testing sample/buildConstructing a list of testsDone constructing a list of testsUpdating test list for fixturesAdded 0 tests to meet fixture requirementsChecking test dependency graph...Checking test dependency graph end

在這里我執(zhí)行了名為Usage的測試。

它以無參數(shù)的方式調用Producer

test 3    Start 3: Usage3: Test command: /home/stephan/Documents/cpp testing sample/build/Producer

輸出不匹配[^[0-9]+]的正則模式,測試未通過。

3: Enter the number of elements as argument1/1 test #3. Usage ................Failed Required regular expression not found.Regex=[^[0-9]+]0.00 sec round.0% tests passed, 1 tests failed out of 1Total Test time (real) =0.00 secThe following tests FAILED:3 - Usage (Failed)Errors while running CTest$

如果想要執(zhí)行所有測試(包括那些用 GoogleTest 生成的),切換到build目錄中,然后運行ctest即可:

CTest run

在 VSCodium 中可以通過點擊信息欄的黃框處來調用 CTest。如果所有測試都通過了,你會看到如下輸出:

VSCodium

使用 Git 鉤子進行自動化測試

目前為止,運行測試是開發(fā)者需要額外執(zhí)行的步驟,那些不能通過測試的代碼仍然可能被提交和推送到代碼倉庫中。利用Git 鉤子可以自動執(zhí)行測試,從而防止有瑕疵的代碼被提交。

切換到.git/hooks目錄,創(chuàng)建pre-commit文件,復制粘貼下面的代碼:

#!/usr/bin/sh(cd build; ctest --output-on-failure -j6)

然后,給文件增加可執(zhí)行權限:

$ chmod +x pre-commit

這個腳本會在提交之前調用 CTest 進行測試。如果有測試未通過,提交過程就會被終止:

Commit failed

只有所有測試都通過了,提交過程才會完成:

Commit succeeded

這個機制也有一個漏洞:可以通過git commit --no-verify命令繞過測試。解決辦法是配置構建服務器,這能保證只有正常工作的代碼才能被提交,但這又是另一個話題了。

總結

本文提到的技術實施簡單,并且能夠幫你快速發(fā)現(xiàn)代碼中的問題。做單元測試可以提高代碼質量,同時也不會打斷你的工作流。GoogleTest 框架提供了豐富的特性以應對各種測試場景,文中我所提到的只是一小部分而已。如果你想進一步了解 GoogleTest,我推薦你閱讀GoogleTest Primer。

關鍵詞:

相關新聞

Copyright 2015-2020   三好網  版權所有 聯(lián)系郵箱:435 22 [email protected]  備案號: 京ICP備2022022245號-21