Redian新闻
>
使用 GoogleTest 和 CTest 进行单元测试 | Linux 中国

使用 GoogleTest 和 CTest 进行单元测试 | Linux 中国

科技
 
导读:进行单元测试可以提高代码质量,并且它不会打断你的工作流。                             
本文字数:6638,阅读时长大约:8分钟

本文是 使用 CMake 和 VSCodium 设置一个构建系统 的后续文章。

在上一篇文章中我介绍了基于 VSCodium🔗 vscodium.com 和 CMake🔗 cmake.org 配置构建系统。本文我将介绍如何通过 GoogleTest🔗 github.com 和 CTest🔗 cmake.org 将单元测试集成到这个构建系统中。

首先克隆 这个仓库🔗 github.com,用 VSCodium 打开,切换到 devops_2 标签。你可以通过点击 main 分支符号(红框处),然后选择 devops_2 标签(黄框处)来进行切换:

VSCodium tag

或者你可以通过命令行来切换:

  1. $ git checkout tags/devops_2

GoogleTest

GoogleTest 是一个平台无关的开源 C++ 测试框架。单元测试是用来验证单个逻辑单元的行为的。尽管 GoogleTest 并不是专门用于单元测试的,我将用它对 Generator 库进行单元测试。

在 GoogleTest 中,测试用例是通过断言宏来定义的。断言可能产生以下结果:

◈ 成功: 测试通过。
◈ 非致命失败: 测试失败,但测试继续。
◈ 致命失败: 测试失败,且测试终止。

致命断言和非致命断言通过不同的宏来区分:

◈ ASSERT_*: 致命断言,失败时终止。
◈ EXPECT_*: 非致命断言,失败时不终止。

谷歌推荐使用 EXPECT_* 宏,因为当测试中包含多个的断言时,它允许继续执行。断言有两个参数:第一个参数是测试分组的名称,第二个参数是测试自己的名称。Generator 只定义了 generate(...) 函数,所以本文中所有的测试都属于同一个测试组:GeneratorTest

针对 generate(...) 函数的测试可以从 GeneratorTest.cpp🔗 github.com 中找到。

引用一致性检查

generate(...)🔗 github.com 函数有一个 std::stringstream🔗 en.cppreference.com 的引用作为输入参数,并且它也将这个引用作为返回值。第一个测试就是检查输入的引用和返回的引用是否一致。

  1. TEST(GeneratorTest, ReferenceCheck){
  2.     const int NumberOfElements = 10;
  3.     std::stringstream buffer;
  4.     EXPECT_EQ(
  5.         std::addressof(buffer),
  6.         std::addressof(Generator::generate(buffer, NumberOfElements))
  7.     );
  8. }

在这个测试中我使用 std::addressof🔗 en.cppreference.com 来获取对象的地址,并用 EXPECT_EQ 来比较输入对象和返回对象是否是同一个。

检查元素个数

本测试检查作为输入的 std::stringstream 引用中的元素个数与输入参数中指定的个数是否相同。

  1. TEST(GeneratorTest, NumberOfElements){
  2.     const int NumberOfElements = 50;
  3.     int nCalcNoElements = 0;
  4.     std::stringstream buffer;
  5.     Generator::generate(buffer, NumberOfElements);
  6.     std::string s_no;
  7.     while(std::getline(buffer, s_no, ' ')) {
  8.         nCalcNoElements++;
  9.     }
  10.     EXPECT_EQ(nCalcNoElements, NumberOfElements);
  11. }

乱序重排

本测试检查随机化引擎是否工作正常。如果连续调用两次 generate 函数,应该得到的是两个不同的结果。

  1. TEST(GeneratorTest, Shuffle){
  2.     const int NumberOfElements = 50;
  3.     std::stringstream buffer_A;
  4.     std::stringstream buffer_B;
  5.     Generator::generate(buffer_A, NumberOfElements);
  6.     Generator::generate(buffer_B, NumberOfElements);
  7.     EXPECT_NE(buffer_A.str(), buffer_B.str());
  8. }

求和校验

与前面的测试相比,这是一个大体量的测试。它检查 1 到 n 的数值序列的和与乱序重排后的序列的和是否相等。 generate(...) 函数应该生成一个 1 到 n 的乱序的序列,这个序列的和应当是不变的。

  1. TEST(GeneratorTest, CheckSum){
  2.     const int NumberOfElements = 50;
  3.     int nChecksum_in = 0;
  4.     int nChecksum_out = 0;
  5.     std::vector<int> vNumbersRef(NumberOfElements); // Input vector
  6.     std::iota(vNumbersRef.begin(), vNumbersRef.end(), 1); // Populate vector
  7.     // Calculate reference checksum
  8.     for(const int n : vNumbersRef){
  9.         nChecksum_in += n;
  10.     }
  11.     std::stringstream buffer;
  12.     Generator::generate(buffer, NumberOfElements);
  13.     std::vector<int> vNumbersGen; // Output vector
  14.     std::string s_no;
  15.     // Read the buffer back back to the output vector
  16.     while(std::getline(buffer, s_no, ' ')) {
  17.         vNumbersGen.push_back(std::stoi(s_no));
  18.     }
  19.     // Calculate output checksum
  20.     for(const int n : vNumbersGen){
  21.         nChecksum_out += n;
  22.     }
  23.     EXPECT_EQ(nChecksum_in, nChecksum_out);
  24. }

你可以像对一般 C++ 程序一样调试这些测试。

CTest

除了嵌入到代码中的测试之外,CTest🔗 cmake.org 提供了可执行程序的测试方式。简而言之就是通过给可执行程序传入特定的参数,然后用 正则表达式🔗 en.wikipedia.org 对它的输出进行匹配检查。通过这种方式可以很容易检查程序对于不正确的命令行参数的反应。这些测试定义在顶层的 CMakeLists.txt🔗 github.com 文件中。下面我详细介绍 3 个测试用例:

参数正常

如果输入参数是一个正整数,程序应该输出应该是一个数列:

  1. add_test(NAME RegularUsage COMMAND Producer 10)
  2. set_tests_properties(RegularUsage
  3.     PROPERTIES PASS_REGULAR_EXPRESSION "^[0-9 ]+"
  4. )

没有提供参数

如果没有传入参数,程序应该立即退出并提示错误原因:

  1. add_test(NAME NoArg COMMAND Producer)
  2. set_tests_properties(NoArg
  3.     PROPERTIES PASS_REGULAR_EXPRESSION "^Enter the number of elements as argument"
  4. )

参数错误

当传入的参数不是整数时,程序应该退出并报错。比如给 Producer 传入参数 ABC

  1. add_test(NAME WrongArg COMMAND Producer ABC)
  2. set_tests_properties(WrongArg
  3.     PROPERTIES PASS_REGULAR_EXPRESSION "^Error: Cannot parse"
  4. )

执行测试

可以使用 ctest -R Usage -VV 命令来执行测试。这里给 ctest 的命令行参数:

◈ -R <测试名称> : 执行单个测试
◈ -VV:打印详细输出

测试执行结果如下:

  1. $ ctest -R Usage -VV
  2. UpdatecTest Configuration from :/home/stephan/Documents/cpp_testing sample/build/DartConfiguration.tcl
  3. UpdateCTestConfiguration from :/home/stephan/Documents/cpp_testing sample/build/DartConfiguration.tcl
  4. Test project /home/stephan/Documents/cpp_testing sample/build
  5. Constructing a list of tests
  6. Done constructing a list of tests
  7. Updating test list for fixtures
  8. Added 0 tests to meet fixture requirements
  9. Checking test dependency graph...
  10. Checking test dependency graph end

在这里我执行了名为 Usage 的测试。

它以无参数的方式调用 Producer

  1. test 3
  2.     Start 3: Usage
  3. 3: Test command: /home/stephan/Documents/cpp testing sample/build/Producer

输出不匹配 [^[0-9]+] 的正则模式,测试未通过。

  1. 3: Enter the number of elements as argument
  2. 1/1 test #3. Usage ................
  3. Failed Required regular expression not found.
  4. Regex=[^[0-9]+]
  5. 0.00 sec round.
  6. 0% tests passed, 1 tests failed out of 1
  7. Total Test time (real) =
  8. 0.00 sec
  9. The following tests FAILED:
  10. 3 - Usage (Failed)
  11. Errors while running CTest
  12. $

如果想要执行所有测试(包括那些用 GoogleTest 生成的),切换到 build 目录中,然后运行 ctest 即可:

CTest run

在 VSCodium 中可以通过点击信息栏的黄框处来调用 CTest。如果所有测试都通过了,你会看到如下输出:

VSCodium

使用 Git 钩子进行自动化测试

目前为止,运行测试是开发者需要额外执行的步骤,那些不能通过测试的代码仍然可能被提交和推送到代码仓库中。利用 Git 钩子🔗 git-scm.com 可以自动执行测试,从而防止有瑕疵的代码被提交。

切换到 .git/hooks 目录,创建 pre-commit 文件,复制粘贴下面的代码:

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

然后,给文件增加可执行权限:

  1. $ chmod +x pre-commit

这个脚本会在提交之前调用 CTest 进行测试。如果有测试未通过,提交过程就会被终止:

Commit failed

只有所有测试都通过了,提交过程才会完成:

Commit succeeded

这个机制也有一个漏洞:可以通过 git commit --no-verify 命令绕过测试。解决办法是配置构建服务器,这能保证只有正常工作的代码才能被提交,但这又是另一个话题了。

总结

本文提到的技术实施简单,并且能够帮你快速发现代码中的问题。做单元测试可以提高代码质量,同时也不会打断你的工作流。GoogleTest 框架提供了丰富的特性以应对各种测试场景,文中我所提到的只是一小部分而已。如果你想进一步了解 GoogleTest,我推荐你阅读 GoogleTest Primer🔗 google.github.io

(题图:MJ/f212ce43-b60b-4005-b70d-8384f2ba5860)


via: https://opensource.com/article/22/1/unit-testing-googletest-ctest

作者:Stephan Avenwedde 选题:lujun9972 译者:toknow-gh 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

LCTT 译者 :Xiangbin Ma
🌟🌟
翻译: 11.0 篇
|
贡献: 3315 天
2014-07-01
2023-07-29
https://linux.cn/lctt/toknow-gh
欢迎遵照 CC-BY-SA 协议规定转载,
如需转载,请在文章下留言 “转载:公众号名称”,
我们将为您添加白名单,授权“转载文章时可以修改”。


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
Brand new Google Nest Mini Smart Speaker ChalkGoogle 已经被OpenAI 超越了吗?| AlphaGo 之父深度访谈Google 下一款 ChatGPT 竞品曝光:投入或超GPT-4,AlphaGo 成秘密武器精选MLE岗位 | Google、Apple 等公司持续热招!注意!2024北美秋招已开启,Google/Meta网申已关Copilot Chat 推出公开测试版,GitHub:花费数月的单元测试成为过去iOS 17新功能让改密码能「反悔」/英特尔不再使用酷睿i系列命名/Google要求员工慎用AI放大招!Meta挑战OpenAI和Google,发布新AI模型!救命!Google、Meta等大厂都疯了!精选DS岗位 | Google、ZOOM、Bank of America持续热招!精选DS岗位 | IBM、Google、Tesla公司岗位发布!曝PwC向Google泄露政府税改机密!Google已承认涉案Global luxury goods market accelerated after record 2022OpenAI、Google和Meta 的当红华人研究员们在想些什么|对话实录地中海邮轮之旅(7):Gibraltar 直布罗陀之二 (照片待加,未完成!)科技巨头年薪曝光!Meta反超Google排第一!Threads用户破亿,会是下一个Google+吗?OpenAI 和 Meta 被起诉;中国通缩风险加剧..你猜,为什么Google和Facebook不用Docker?大战开启:Meta将Google踹下神坛!Google 25 周年| 2.0——从「X」到 「Alphabet」多瑙河岸景,城堡大观带着针线去探亲-----后续Google 团队推出作图工具 StyleDrop,AI 画图更加可控Meta上线新品,抢Twitter用户/小米汽车新进展曝光/Google公布量子计算机新突破Google Nest 高颜值 家用智能温控器7.2折 129.99元!刚砸了Meta饭碗,Apple又惹到了Google……大模型颠覆研发模式:字节跳动是如何在单元测试中落地大模型的?Hume/ㄏㄩㄇ/休谟道德哲学译注智能周报|Meta和Google把对标GPT-4提上日程;英伟达在AI领域有20笔投资;英国初创公司推出能解释行为的自动驾驶系统长篇小说《如絮》 第一百二十四章 越南-1964年 SOGMeta反超Google:开出$300000价码!字节跳动如何在单元测试中落地大模型|QConBazzite:专为 Steam Deck 和 PC 上的 Linux 游戏打造的发行版 | Linux 中国雪上加霜!Meta之后,Google明年也要抛弃旧金山了!5个关键问题让单元测试的价值最大化
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。