ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

gperftools 项目内嵌 GoogleTest 官方示例全解析:从基础断言到 Listener 高级定制

gperftools 项目内嵌 GoogleTest 官方示例全解析:从基础断言到 Listener 高级定制 性能剖析内存管理开发工具【免费下载链接】gperftoolsMain gperftools repository项目地址https://gitcode.com/gh_mirrors/gp/gperftools点击查看免费下载本指南以 gperftools 仓库中内嵌的 GoogleTest 官方示例文档samples.md为骨架逐一对 Sample #1 ~ #10 进行讲解并结合 samples/ 目录下的完整源码展开实现细节。gperftools 自身的单元测试如 src/tests/ 下的 tcmalloc_unittest.cc、page_heap_test.cc 等正是基于这套内嵌的 GoogleTest 构建与运行的。读完本文你将能够理解 GoogleTest 断言、测试固件Fixture、类型/值参数化测试的核心用法掌握通过 Listener API 与反射 API 定制测试输出、实现简易内存泄漏检测的完整套路并知道如何在 gperftools 仓库中通过 CMake 直接编译运行这些示例。示例总览与仓库布局vendor/googletest/docs/samples.md是一个精炼的索引页它没有长篇论述而是用 10 条要点概括了 googletest/samples 目录下十个示例各自演示的主题。这十个示例在仓库中的实体文件位于 vendor/googletest/googletest/samples/示例主题核心文件本仓库内Sample #1基础步骤用 GoogleTest 测试 C 函数sample1.h、sample1.cc、sample1_unittest.ccSample #2对含多个成员函数的类编写更复杂的单元测试sample2.h、sample2_unittest.ccSample #3使用测试固件test fixturesample3-inl.h、sample3_unittest.ccSample #4将 GoogleTest 与googletest.h即 gtest 头文件协同使用sample4.h、sample4.cc、sample4_unittest.ccSample #5把共享测试逻辑放进基类固件在派生固件中复用sample5_unittest.ccSample #6类型参数化测试typed testssample6_unittest.ccSample #7值参数化测试value-parameterized tests基础sample7_unittest.ccSample #8值参数化测试中使用Combine()sample8_unittest.ccSample #9Listener API 修改控制台输出 反射 API 检查测试结果sample9_unittest.ccSample #10Listener API 实现简易内存泄漏检测器sample10_unittest.cc其中 Sample #6、#7、#8 共用同一个被测接口 prime_tables.hPrimeTable接口及OnTheFlyPrimeTable、PreCalculatedPrimeTable两个实现用来演示“接口测试”interface tests——即对同一接口的多个实现跑同一套测试。Sample #3、#5 共用Queue模板类sample3-inl.hSample #1、#5 共用Factorial/IsPrimesample1.h。在 gperftools 仓库的顶层 CMakeLists.txt 中gtest 被作为静态库编译CMakeLists.txt仓库自身的tcmalloc_minimal_unittest、page_heap_test等测试目标均链接了gtest说明这套内嵌 GoogleTest 就是 gperftools 测试基础设施的一部分而十个 sample 的编译入口则由 vendor/googletest/googletest/CMakeLists.txt 的gtest_build_samples选项控制。构建与运行示例的前提示例默认不参与构建。按照 vendor/googletest/googletest/CMakeLists.txt 中的说明需要通过-Dgtest_build_samplesON开启# 在 gperftools 仓库根目录执行 cmake -Dgtest_build_samplesON -B build_samples . cmake --build build_samples --target sample1_unittest sample10_unittest从 CMake 脚本可见每个示例的链接方式CMakeLists.txt目标链接方式说明sample1_unittest / sample2_unittest / sample3_unittest / sample4_unittest / sample5_unittestgtest_main链接预置 main 的 gtest_main免写main()sample6_unittest / sample7_unittest / sample8_unittestgtest_main同上依赖prime_tables.h与 prime_tables.hsample9_unittest / sample10_unittestgtest自行提供main()用于处理自定义命令行参数gtest_main提供的main()只是调用RUN_ALL_TESTS()并返回 0/1详见 sample1_unittest.cc 的注释这也是“三步写测试”的最后一步。Sample #1三步写出第一个单元测试Sample #1 演示的是 GoogleTest 最基础的用法。被测函数在 sample1.ccFactorial(n)返回 n 的阶乘负数为 1IsPrime(n)判断素数先排除 ≤1 和偶数再从 3 开始只试奇数到平方根为止。对应的 sample1_unittest.cc 给出了“1-2-3”三步流程包含必要头文件#include gtest/gtest.h声明测试框架同时包含被测代码sample1.h用TEST宏定义测试TEST(FactorialTest, Negative)这样的形式包含两个参数——测试用例名case/suite 名与测试名二者都必须是合法 C 标识符且不建议使用下划线测试逻辑写在宏后的大括号内调用RUN_ALL_TESTS()链接gtest_main即可自动获得入口无需手工注册测试宏机制会保证每个测试恰好运行一次但不保证执行顺序因此测试结果不应依赖运行次序。测试体中的断言值得注意sample1_unittest.ccTEST(FactorialTest, Negative) { EXPECT_EQ(1, Factorial(-5)); EXPECT_EQ(1, Factorial(-1)); EXPECT_GT(Factorial(-10), 0); }EXPECT_EQ(expected, actual)等价于EXPECT_TRUE((expected) (actual))但失败时会把期望值与实际值同时打印出来便于调试因此优先选用EXPECT_TRUE接受任意布尔表达式更通用EXPECT_FALSE同理用于断言为假同一用例case内可放逻辑相关的多个测试例如FactorialTest用例下就有Negative、Zero、Positive三个测试分别覆盖负数、0、正数三条路径IsPrimeTest用例则覆盖负输入含INT_MIN、平凡情形0/1/2/3与正输入含 23 这类素数。Sample #2类的多成员函数测试Sample #2 演示对一个拥有多个成员函数的类如何组织测试。被测类是MyString见 sample2.h其测试分布在 sample2_unittest.cc 的MyString用例下默认构造函数、接受 C 字符串的构造函数、拷贝构造函数、Set()方法各有一个TEST即“一个方法对应一个测试”的组织思路。该示例还展示了两个实用技巧EXPECT_STREQ(nullptr, s.c_string())用于比较 C 字符串注意nullptr应写成指针形式static_castconst char*(NULL)亦可否则EXPECT_EQ因NULL被宏定义为整数0而可能触发编译警告sample2_unittest.ccEXPECT_EQ(0, strcmp(...))与EXPECT_EQ(sizeof(kHelloString)/sizeof(kHelloString[0]) - 1, s.Length())这类对字符串长度、内容的组合断言用于验证Set()在输入指针与对象内部指针相同自赋值、置空Set(nullptr)等边界场景下的行为。Sample #3测试固件Fixture消除重复代码Sample #3 引入测试固件test fixture其核心思想记录在 sample3_unittest.cc 的注释中固件是存放一个测试用例内所有测试共享对象与函数的地方避免为每个测试重复编写初始化与清理代码。被测对象是QueueE单链表模板sample3-inl.h包含Enqueue、Dequeue、Map等方法。固件使用四步从testing::Test派生子类如QueueTestSmpl3成员用protected修饰以便子类访问重写SetUp()做初始化在每个测试运行前调用重写TearDown()做清理在每个测试结束后调用非必需在固件中声明测试要用的成员变量与辅助函数用TEST_F(QueueTestSmpl3, TestName)代替TEST定义测试TEST_F的第一个参数必须是固件类名。关键设计点sample3_unittest.cc固件是代码共享而非数据共享每个测试都获得一份全新的固件实例一个测试修改的数据不会传给下一个测试这正是“测试应独立、可重复”这一原则的体现——一个测试不应因另一个测试的失败而失败断言宏EXPECT_TRUE、FAIL等在内部会调用Test类的成员函数以确定“当前测试是谁”因此不能在全局函数中使用这些宏需要共享的测试子例程应放在固件内如示例中的MapTester辅助函数固件内可以同时使用ASSERT_EQ失败即中止当前测试与EXPECT_EQ失败后继续执行例如Dequeue测试先ASSERT_TRUE(n ! nullptr)再解引用指针避免空指针崩溃掩盖后续断言sample3_unittest.cc。Sample #4被测代码与 GoogleTest 的协作Sample #4 篇幅最短但点明了一个集成要点把被测类Counter见 sample4.h的头文件与gtest/gtest.h一起包含即可同时获得被测代码声明与测试框架能力。其测试sample4_unittest.cc直接验证Increment()/Decrement()的自增自减与边界0 减 1 仍为 0并强调EXPECT_EQ()的参数只求值一次因此可以安全传入带副作用的表达式如c.Increment()的返回值。TEST(Counter, Increment) { Counter c; EXPECT_EQ(0, c.Decrement()); // 0 减 1 仍返回 0 EXPECT_EQ(0, c.Increment()); EXPECT_EQ(1, c.Increment()); EXPECT_EQ(2, c.Increment()); EXPECT_EQ(3, c.Decrement()); }这一示例的可运行形态是sample4_unittest目标由 sample4.cc 与gtest_main一起编译链接CMakeLists.txt。Sample #5基类固件 派生固件复用共享逻辑Sample #5 解决这样一个问题固件在定义时就绑定了用例名因此一个固件只能服务一个用例当多个用例需要相同或相近的固件时应把共享逻辑放进超类固件super fixture再由各用例使用派生固件。sample5_unittest.cc 中的QuickTest是超类固件SetUp()记录开始时间TearDown()断言end_time - start_time 5从而为所有继承它的测试自动施加“单测试不得超过约 5 秒”的约束。随后class IntegerFunctionTest : public QuickTest { /* 空体即可 */ }; TEST_F(IntegerFunctionTest, Factorial) { EXPECT_EQ(1, Factorial(-5)); ... }超类固件本身不需要有同名用例示例注释明确指出不存在QuickTest用例是正常的。派生固件还可以继续叠加自己的逻辑——例如针对队列测试的固件在QuickTest之上加入队列数据准备最终得到“既有超时约束、又有各自初始化”的多层测试固件。该示例同时复用了 Sample #1 的Factorial与 Sample #3 的Queuesample5_unittest.cc编译时以gtest_main加 sample1.cc 链接CMakeLists.txt。Sample #6类型参数化测试Typed TestsSample #6 演示“对同一接口的多个实现跑同一套测试”interface tests。被测接口 prime_tables.h 定义PrimeTable其两个实现分别为OnTheFlyPrimeTable运行时逐个判定素数与PreCalculatedPrimeTable构造时预计算并缓存素数表GetNextPrime超界返回 -1。类型参数化测试的写法sample6_unittest.cc定义固件类模板template class T class PrimeTableTest : public testing::Test通过模板特化的工厂函数CreatePrimeTableT()在构造函数中创建被测对象并经基类接口PrimeTable*持有——这样测试贴近真实场景还能避开派生类方法遮蔽基类同名方法签名略有差异的陷阱用TYPED_TEST_SUITE(PrimeTableTest, Implementations)声明类型列表其中Implementations是TypesOnTheFlyPrimeTable, PreCalculatedPrimeTable类型列表用TYPED_TEST(PrimeTableTest, TestName)定义测试在测试体内类型参数以TypeParam引用固件类以TestFixture引用且因身处模板世界访问固件成员必须显式写this-table_GoogleTest 会为类型列表中的每个类型自动重复执行每个TYPED_TEST无需手写循环。适用于“写测试时已知全部被测类型”的场景若类型集合是运行时才确定的则应改用下一节的值参数化测试。Sample #7值参数化测试基础Sample #7 同样做接口测试但参数是运行时的值这里是被测对象工厂函数指针。写法sample7_unittest.cc从TestWithParamT派生固件class PrimeTableTestSmpl7 : public TestWithParamCreatePrimeTableFunc*在固件SetUp()中用GetParam()取参数并创建被测对象table_ (*GetParam())();TearDown()中销毁——每个测试独立创建、销毁被测对象避免测试间相互影响用TEST_P(PrimeTableTestSmpl7, TestName)定义测试测试体内同样通过GetParam()访问参数关键一步是实例化INSTANTIATE_TEST_SUITE_P(OnTheFlyAndPreCalculated, PrimeTableTestSmpl7, Values(CreateOnTheFlyPrimeTable, CreatePreCalculatedPrimeTable))把测试绑定到一组参数值上可以在不同翻译单元多次实例化。Values(...)来自::testing::Values用于枚举离散参数值。未实例化的TEST_P不会被运行这是与普通测试最大的区别。Sample #8用 Combine() 生成参数组合Sample #8 演示值参数化测试的进阶用法用Combine()把多组参数两两组合生成笛卡尔积。示例的被测类是HybridPrimeTablesample8_unittest.cc它内部同时持有OnTheFlyPrimeTable与PreCalculatedPrimeTable低内存时可强制只使用前者。固件参数为TestWithParam ::std::tuplebool, int 其中bool表示是否强制 on-the-flyint表示预计算表容量SetUp()用std::tie解包GetParam()构造被测对象sample8_unittest.cc。实例化时INSTANTIATE_TEST_SUITE_P(MeaningfulTestParameters, PrimeTableTest, Combine(Bool(), Values(1, 10, 100)));Bool()生成true/false两个取值Values(1, 10, 100)生成三个容量取值Combine()生成 2×36 个tuple参数覆盖“预计算表启用/禁用 × 容量大小”的全部组合从而让同一批TEST_P遍历HybridPrimeTable的所有代码路径数字落在表容量内/外、表被禁用。Sample #9Listener API 定制输出 反射 API 检查结果Sample #9 展示两条高级能力sample9_unittest.cc。Listener API——替换默认控制台输出自定义监听器TersePrinter继承EmptyTestEventListener按需重写生命周期回调OnTestProgramStart/OnTestProgramEnd整个测试程序开始/结束后者打印TEST PASSED/TEST FAILEDOnTestStart/OnTestEnd单个测试开始/结束可拿到TestInfo的test_suite_name()与name()OnTestPartResult某个断言结果产生时触发从TestPartResult可读取file_name()、line_number()、summary()、failed()等信息实现“极简输出模式”。安装与拆卸监听器的标准流程在main()中sample9_unittest.ccUnitTest unit_test *UnitTest::GetInstance(); TestEventListeners listeners unit_test.listeners(); // 1) 移除默认结果打印器所有权转移给调用方需 delete delete listeners.Release(listeners.default_result_printer()); // 2) 追加自定义监听器此后所有权归 GoogleTest无需 delete listeners.Append(new TersePrinter);反射 API——枚举并检查测试结果UnitTest::GetInstance()是单例入口通过total_test_suite_count()/GetTestSuite(i)遍历用例TestSuite再经total_test_count()/GetTestInfo(j)遍历用例内测试TestInfo最后用test_info.result()-Failed()判断是否失败。示例据此统计“意外失败”的测试数——名字不含Fails却失败了的测试——并据此修正进程返回值sample9_unittest.cc。由于示例自定义了main()处理--terse_output参数因此编译链接gtest而非gtest_mainCMakeLists.txt。运行方式./sample9_unittest # 默认输出 ./sample9_unittest --terse_output # 启用 TersePrinter 定制输出Sample #10Listener API 实现简易内存泄漏检测Sample #10 展示 Listener API 的实用价值实现一个原始的内存泄漏检查器sample10_unittest.cc。思路分三层被监测类型Water类重载operator new/operator delete用静态计数器allocated_记录存活对象数sample10_unittest.cc监听器LeakChecker : public EmptyTestEventListener重写OnTestStart记录开始时存活数与OnTestEnd比较结束时存活数若difference 0则EXPECT_LE(difference, 0) Leaked ... unit(s) of Water!报告泄漏。注释特别提醒任何事件处理器中都可以产生失败唯独OnTestPartResult回调里不行会递归所以这里用标准断言即可sample10_unittest.cc安装在自定义main()中解析--check_for_leaks参数为真时向listeners()追加LeakChecker于是LeaksWater测试new 了 Water 却未 delete在开启该开关时会如预期失败而DoesNotLeak正常通过sample10_unittest.cc。./sample10_unittest --check_for_leaks # 启用自定义泄漏检查这一“对象计数 事件监听”模式在 gperftools 自己的测试中也可见到类似的资源检查思路——仓库中大量内存相关单测如 src/tests/tcmalloc_unittest.cc、src/tests/page_heap_test.cc都通过内嵌的 gtest 断言内存分配行为而 gperftools 自身还提供更专业的内存泄漏检测工具heap-checker.h可作为生产环境下的更完整方案。十个示例的能力矩阵与选型建议需求选用示例关键宏/API测试独立函数#1TEST、EXPECT_EQ、EXPECT_TRUE/FALSE、EXPECT_GT测试类多个成员函数#2TESTEXPECT_STREQ、strcmp组合断言多测试共享初始化/清理#3TEST_F、SetUp/TearDown、ASSERT_EQ被测类与框架头文件协作#4同时包含被测头文件与gtest/gtest.h跨用例复用固件逻辑#5基类固件 派生固件 TEST_F已知全部类型的接口测试#6TYPED_TEST_SUITE、TYPED_TEST、Types运行时参数化的接口测试#7TEST_P、INSTANTIATE_TEST_SUITE_P、Values多参数组合穷举#8Combine、Bool、std::tuple定制输出与结果检查#9TestEventListener、UnitTest/TestSuite/TestInfo反射遍历资源泄漏检测#10EmptyTestEventListener 对象计数总的原则测试应独立、可重复、不依赖执行顺序断言失败信息要足够定位问题EXPECT_*优于裸EXPECT_TRUE共享逻辑放固件而非全局函数参数化让一套测试逻辑覆盖多个实现或多种参数组合Listener 与反射 API 则把测试框架从“跑断言”扩展为“构建自定义测试基础设施”。延伸阅读官方文档索引与 FAQvendor/googletest/docs/index.md、vendor/googletest/docs/faq.md入门基础vendor/googletest/docs/primer.md进阶特性vendor/googletest/docs/advanced.mdGoogleMock 系列vendor/googletest/docs/gmock_for_dummies.md、vendor/googletest/docs/gmock_cook_book.md、vendor/googletest/docs/gmock_cheat_sheet.md在 gperftools 仓库中的实际应用浏览 src/tests/ 目录下的tcmalloc_unittest.cc、page_heap_test.cc、realloc_unittest.cc等可以看到这些示例所演示的断言、固件与参数化手法如何被真实项目大规模使用gtest 目标的编译配置见顶层 CMakeLists.txt。赞分享性能剖析内存管理开发工具【免费下载链接】gperftoolsMain gperftools repository项目地址https://gitcode.com/gh_mirrors/gp/gperftools点击查看免费下载相关推荐GoogleTest断言机制全面解析从基础到高级用法GoogleTest断言机制全面解析从基础到高级用法 概述 GoogleTest作为C领域广泛使用的单元测试框架其断言机制是测试代码的核心组成部分。本文测试质量保障开发工具Google Test 样例全解析从基础断言到 Listener 扩展miniblink49 仓库 v8_7_5 内嵌 gtest 实践指南Google Test 样例全解析从基础断言到 Listener 扩展miniblink49 仓库 v8_7_5 内嵌 gtest 实践指南 Google前端桌面应用深入GoogleTest断言系统从基础到高级技巧深入GoogleTest断言系统从基础到高级技巧 本文深入探讨GoogleTest框架中丰富的断言系统从基础的EXPECT_ 与ASSERT_ 断言区别与应测试上一篇ResNet-50图像分类实战从原理到部署的创新路径下一篇Xcode 快捷键速查清单搜索、导航、调试与构建的全流程操作指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表