ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

GoogleTest 断言宏完全参考:从 EXPECT/ASSERT 家族到浮点、异常与 Death Test 的实现剖析

2026/9/6 19:52:20 拓冰建站 浏览量
GoogleTest 断言宏完全参考:从 EXPECT/ASSERT 家族到浮点、异常与 Death Test 的实现剖析 GoogleTest 断言宏完全参考从 EXPECT/ASSERT 家族到浮点、异常与 Death Test 的实现剖析【免费下载链接】googletestGoogleTest - Google Testing and Mocking Framework项目地址: https://gitcode.com/GitHub_Trending/go/googletestGoogleTest 的断言宏assertion macros是编写 C 单元测试的核心工具覆盖布尔条件、二元比较、C 字符串、浮点数、异常、谓词以及进程终止等多种验证场景。本文基于当前仓库的 Assertions Reference 逐类完整整理全部宏的用法与语义并结合 gtest.h 与 gtest-internal.h 中的实现源码解释“EXPECT_ 与 ASSERT_ 的区别从哪里来”“4 ULP 容差在代码中的位置”等底层细节帮助你在实际项目中正确选择并深入理解每一个断言宏。使用断言宏的基本约定要使用本文涉及的断言宏需要在测试文件中包含#include gtest/gtest.h文档中列出的大部分宏都成对出现EXPECT_变体与ASSERT_变体。二者语义的关键区别是EXPECT_宏失败时生成非致命nonfatal失败当前函数会继续向下执行后续的断言仍会运行ASSERT_宏失败时生成致命fatal失败直接终止当前函数以return方式离开。因此ASSERT_宏只能用于返回void的函数中——如果函数有返回值return;将导致编译错误。这一约束在使用ASSERT_HRESULT_SUCCEEDED等宏于非 void 函数时尤其需要注意详见 Advanced Guide 的 Assertion Placement 章节。所有断言宏都支持通过运算符流式追加自定义失败消息例如EXPECT_TRUE(my_condition) My condition is not true;凡是能流向ostream的内容都可以流向断言宏尤其是 C 字符串和字符串对象。如果向断言流式输出宽字符串wchar_t*、WindowsUNICODE模式下的TCHAR*、或std::wstring输出时会被转译为 UTF-8 打印。从源码结构看这种流式消息能力由 gtest.h 中的internal::AssertHelper类实现断言宏展开后构造一个AssertHelper临时对象表达式通过其operator(const Message)语义技巧把消息交给断言框架析构时统一上报。源码注释还特意提到把数据放在独立struct中以压缩AssertHelper对象大小因为 GCC 不会复用临时变量的栈空间每个EXPECT_EQ都会为AssertHelper预留栈开销。显式成功与失败这一类断言不测试某个值或表达式而是直接生成成功或失败适用于由控制流而非布尔表达式决定测试成败的场景switch (expression) { case 1: ... some checks ... case 2: ... some other checks ... default: FAIL() We shouldnt get here.; }SUCCEED()SUCCEED()显式生成一个“成功”。注意它并不会让整体测试变为成功——只有测试执行期间没有任何断言失败该测试才算通过。SUCCEED目前纯粹是文档性质的标记不产生任何用户可见输出文档也说明未来可能会在输出中体现SUCCEED消息。FAIL()FAIL()生成一个致命失败随后从当前函数返回。因此它同样只能用于返回void的函数中见 Assertion Placement。ADD_FAILURE()ADD_FAILURE()生成一个非致命失败当前函数继续运行。ADD_FAILURE_AT(file_path, line_number)ADD_FAILURE_AT(file_path,line_number)在指定的文件和行号位置生成一个非致命失败。适合在封装层把失败“指回”真实的问题源头。广义断言EXPECT_THAT 与 MatcherEXPECT_THAT允许用matcher来验证值是把断言写成“类英文句子”的关键设施。EXPECT_THAT(value,matcher)ASSERT_THAT(value,matcher)验证value是否匹配给定的 matcher。例如#include gmock/gmock.h using ::testing::AllOf; using ::testing::Gt; using ::testing::Lt; using ::testing::MatchesRegex; using ::testing::StartsWith; ... EXPECT_THAT(value1, StartsWith(Hello)); EXPECT_THAT(value2, MatchesRegex(Line \\d)); ASSERT_THAT(value3, AllOf(Gt(5), Lt(10)));Matcher 能让这类断言读起来像英文并在失败时生成信息丰富的消息。如果上面针对value1的断言失败输出大致如下Value of: value1 Actual: Hi, world! Expected: starts with Hello仓库内配套提供了内置 matcher 库参见 Matchers Reference对应头文件 gmock-matchers.h 与 gtest-matchers.h自写 matcher 的方法参见 Writing New Matchers Quickly。这套机制的思想源自 Joe Walnes 的 Hamcrest 项目其为 JUnit 提供了assertThat()。布尔条件断言EXPECT_TRUE(condition)/ASSERT_TRUE(condition)验证condition为真。EXPECT_FALSE(condition)/ASSERT_FALSE(condition)验证condition为假。二者是所有“值断言”的最简形态失败消息会直接打印表达式的值。二元比较断言这一类断言比较两个值。值参数必须支持该断言的比较运算符否则会在编译期报错。通用规则文档逐条列出务必注意若参数支持运算符断言失败时会用它打印参数值否则 GoogleTest 会尽力以最佳方式打印——参见 Teaching GoogleTest How to Print Your Values参数恰好求值一次因此参数带副作用也是安全的但参数求值顺序未定义程序不应依赖任何特定顺序同时支持窄字符串与宽字符串对象string与wstring比较浮点数请改用下文 浮点比较 断言避免舍入误差问题。EXPECT_EQ / ASSERT_EQEXPECT_EQ(val1,val2)验证val1 val2。对指针执行指针相等性地址比较。如果用在两个 C 字符串上测试的是它们是否位于同一内存位置而不是内容是否相同。按内容比较 C 字符串请改用EXPECT_STREQ。与NULL比较指针时推荐写EXPECT_EQ(ptr, nullptr)而不是EXPECT_EQ(ptr, NULL)。源码印证EXPECT_EQ最终调用 gtest.h 中的internal::CmpHelperEQ即一次普通的lhs rhs比较失败路径被拆分到CmpHelperEQFailureL1378-L1385以减小热路径栈帧注释中说明这是为了降低在紧密循环中调用EXPECT_*时某些 sanitizer 的开销。此外EqHelper::Compare提供了专门的std::nullptr_t重载L1433-L1441这正是文档推荐nullptr而非NULL/0的原因nullptr重载能确定性地走指针比较路径。EXPECT_NE / ASSERT_NEEXPECT_NE(val1,val2)验证val1 ! val2。与EXPECT_EQ相同的指针语义用于两个 C 字符串时比较的是内存位置是否不同而非内容是否不同按内容比较请用EXPECT_STRNE。与NULL比较指针时推荐EXPECT_NE(ptr, nullptr)。EXPECT_LT / EXPECT_LE / EXPECT_GT / EXPECT_GEEXPECT_LT(val1,val2)/ASSERT_LT(...)验证val1 val2EXPECT_LE(val1,val2)/ASSERT_LE(...)验证val1 val2EXPECT_GT(val1,val2)/ASSERT_GT(...)验证val1 val2EXPECT_GE(val1,val2)/ASSERT_GE(...)验证val1 val2从源码结构看这五个比较宏共用 gtest.h 中GTEST_IMPL_CMP_HELPER_(op_name, op)宏生成的CmpHelperNE/LE/LT/GE/GT系列辅助函数比较成功返回AssertionSuccess()失败则通过CmpHelperOpFailure生成形如Expected: (a) (b), actual: ... vs ...的消息L1447-L1455其中两侧值都经过FormatForComparisonFailureMessage格式化保证失败输出可读。C 字符串比较断言这一类断言比较两个C 字符串const char*等。比较两个std::string对象时应改用EXPECT_EQ/EXPECT_NE。补充语义来自文档也接受宽 C 字符串wchar_t*两个宽字符串比较失败时值会以 UTF-8 窄字符串形式打印要把 C 字符串与NULL比较使用EXPECT_EQ(c_string, nullptr)或EXPECT_NE(c_string, nullptr)。宏语义EXPECT_STREQ(str1, str2)/ASSERT_STREQ(...)两个 C 字符串内容相同EXPECT_STRNE(str1, str2)/ASSERT_STRNE(...)两个 C 字符串内容不同EXPECT_STRCASEEQ(str1, str2)/ASSERT_STRCASEEQ(...)内容相同忽略大小写EXPECT_STRCASENE(str1, str2)/ASSERT_STRCASENE(...)内容不同忽略大小写源码中这些宏对应 gtest.h 里声明的CmpHelperSTREQ/CmpHelperSTRNE/CmpHelperSTRCASEEQ/CmpHelperSTRCASENE并针对wchar_t*提供宽字符重载受GTEST_HAS_STD_WSTRING宏门控与文档所述宽字符串支持一致。浮点比较断言由于舍入误差两个浮点值精确相等的可能性极低因此EXPECT_EQ不适合比较浮点数。有意义的浮点比较通常需要用户仔细选择误差界。GoogleTest 还提供了基于ULPUnits in the Last Place最后一位的单位数的默认误差界断言。EXPECT_FLOAT_EQ / ASSERT_FLOAT_EQ验证两个float值近似相等容差为相互之间不超过 4 个 ULP。实现细节在 gtest-internal.hFloatingPoint类中static const uint32_t kMaxUlps 4;AlmostEquals()L337-L348把浮点数规范化为“符号-幅度”整数表示后检查两者距离是否不超过kMaxUlps。宏入口为 gtest.h 的ASSERT_FLOAT_EQ/EXPECT_FLOAT_EQ底层调用 CmpHelperFloatingPointEQ。文档特别指出Infinity与最大有限float值被视为相距 1 个 ULP。EXPECT_DOUBLE_EQ / ASSERT_DOUBLE_EQ同上但面向double两个double值在相互 4 个 ULP 范围内视为相等Infinity与最大有限double值视为相距 1 个 ULP。EXPECT_NEAR / ASSERT_NEAREXPECT_NEAR(val1,val2,abs_error)验证val1与val2的差不超过绝对误差界abs_error。特殊值语义若两值同为同号无穷差视为 0否则只要任一值为无穷差视为无穷所有非 NaN 值含无穷都不超过一个“无穷”的abs_error。实现上对应 gtest.h 中声明的internal::DoubleNearPredFormat辅助函数。异常断言这一类断言验证一段代码是否抛出异常使用前提是编译环境启用了异常C 默认开启C 或禁用异常的构建不可用。被测试的代码可以是复合语句例如EXPECT_NO_THROW({ int n 5; DoSomething(n); });EXPECT_THROW / ASSERT_THROWEXPECT_THROW(statement,exception_type)验证statement抛出类型恰为exception_type的异常。EXPECT_ANY_THROW / ASSERT_ANY_THROWEXPECT_ANY_THROW(statement)验证statement抛出任意类型的异常。EXPECT_NO_THROW / ASSERT_NO_THROWEXPECT_NO_THROW(statement)验证statement不抛出任何异常。仓库自带测试 gtest_assert_by_exception_test.cc 专门覆盖“以异常替代 abort 报告失败”的相关行为可作为异常路径行为的参考实现。谓词断言谓词断言允许验证更复杂的谓词同时比单独使用EXPECT_TRUE产生更清晰的失败消息。EXPECT_PRED1~5 / ASSERT_PRED1~5EXPECT_PRED1(pred, val1) EXPECT_PRED2(pred, val1, val2) EXPECT_PRED3(pred, val1, val2, val3) EXPECT_PRED4(pred, val1, ..., val4) EXPECT_PRED5(pred, val1, ..., val5) // ASSERT_PRED1 ~ ASSERT_PRED5 同理验证谓词pred接收给定值作为实参时返回true。pred是函数或仿函数接受的参数个数与宏接收的值个数一致对给定实参返回true则断言成功否则失败。断言失败时会打印每个实参的值实参恰好求值一次。示例// 当 m 和 n 除 1 之外没有公因子时返回 true。 bool MutuallyPrime(int m, int n) { ... } ... const int a 3; const int b 4; const int c 10; ... EXPECT_PRED2(MutuallyPrime, a, b); // 成功 EXPECT_PRED2(MutuallyPrime, b, c); // 失败第二条断言失败时输出MutuallyPrime(b, c) is false, where b is 4 c is 10文档特别提示了重载/模板谓词的歧义问题若谓词是重载函数或函数模板宏可能无法确定用哪个版本需要显式指定函数类型例如对int/double两个重载的IsPositive()EXPECT_PRED1(static_castbool (*)(int)(IsPositive), 5); EXPECT_PRED1(static_castbool (*)(double)(IsPositive), 3.14);直接写EXPECT_PRED1(IsPositive, 5);会引发编译错误。使用模板函数时需指定模板实参template typename T bool IsNegative(T x) { return x 0; } ... EXPECT_PRED1(IsNegativeint, -5); // 必须指定 IsNegative 的类型若模板有多个参数需给谓词加括号保证宏参数被正确解析ASSERT_PRED2((MyPredicateint, int), 5, 0);EXPECT_PRED_FORMAT1~5 / ASSERT_PRED_FORMAT1~5EXPECT_PRED_FORMAT1(pred_formatter, val1) EXPECT_PRED_FORMAT2(pred_formatter, val1, val2) EXPECT_PRED_FORMAT3(pred_formatter, val1, val2, val3) EXPECT_PRED_FORMAT4(pred_formatter, val1, ..., val4) EXPECT_PRED_FORMAT5(pred_formatter, val1, ..., val5) // ASSERT_PRED_FORMAT1 ~ ASSERT_PRED_FORMAT5 同理验证谓词pred_formatter接收给定值作为实参时“成功”。pred_formatter是一个predicate-formatter其签名为testing::AssertionResult PredicateFormatter(const char* expr1, const char* expr2, ... const char* exprn, T1 val1, T2 val2, ... Tn valn);其中val1..valn是谓词实参的值expr1..exprn是对应表达式在源码中的原始文本。T1..Tn可以是值类型或引用类型——实参类型为T时形参可声明为T或const T。返回类型testing::AssertionResult的用法参见 Using a Function That Returns an AssertionResult。示例// 返回 m 和 n 的最小素公因子m、n 互素时返回 1。 int SmallestPrimeCommonDivisor(int m, int n) { ... } // m、n 除 1 之外无公因子时返回 true。 bool MutuallyPrime(int m, int n) { ... } // 断言两个整数互素的 predicate-formatter。 testing::AssertionResult AssertMutuallyPrime(const char* m_expr, const char* n_expr, int m, int n) { if (MutuallyPrime(m, n)) return testing::AssertionSuccess(); return testing::AssertionFailure() m_expr and n_expr ( m and n ) are not mutually prime, as they have a common divisor SmallestPrimeCommonDivisor(m, n); } ... const int a 3; const int b 4; const int c 10; ... EXPECT_PRED_FORMAT2(AssertMutuallyPrime, a, b); // 成功 EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c); // 失败最后一条断言失败时predicate-formatter 生成的失败消息为b and c (4 and 10) are not mutually prime, as they have a common divisor 2这就是 predicate-formatter 相对普通谓词的价值消息文本由你定制可以把源码表达式与计算出的公因子一并输出。仓库还内置了现成工具gtest.h 中的IsSubstring/IsNotSubstring就是专为EXPECT_PRED_FORMAT2设计的 predicate-formatterNULL 仅当与自身比较时视为子串。Windows HRESULT 断言这一类断言测试HRESULT的成功或失败例如CComPtrIShellDispatch2 shell; ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(LShell.Application)); CComVariant empty; ASSERT_HRESULT_SUCCEEDED(shell-ShellExecute(CComBSTR(url), empty, empty, empty, empty));生成的输出中会包含与返回的HRESULT码相关联的人类可读错误消息。EXPECT_HRESULT_SUCCEEDED(expression)/ASSERT_HRESULT_SUCCEEDED(...)验证expression是成功HRESULTEXPECT_HRESULT_FAILED(expression)/ASSERT_HRESULT_FAILED(...)验证expression是失败HRESULT。Death 断言这一类断言验证一段代码会导致进程终止背景知识参见 Death Tests。工作机制文档原文要点断言会启动一个新进程并在其中执行被测代码。具体方式取决于平台和变量::testing::GTEST_FLAG(death_test_style)它由命令行标志--gtest_death_test_style初始化POSIX 系统用fork()Linux 上为clone()派生子进程随后值为fast时death test 语句在子进程中立即执行值为threadsafe时子进程像最初被调用一样重新执行单元测试二进制但附加额外标志使只有当前这一个 death test 被运行Windows用CreateProcess()API 派生子进程并重新执行二进制只运行当前 death test——与 POSIX 的threadsafe模式类似其他取值非法会导致 death test 失败。该标志当前默认值为fast。如果 death test 语句正常跑完而没有导致进程死亡子进程仍会终止断言判为失败。被测代码同样可以是复合语句EXPECT_DEATH({ int n 5; DoSomething(n); }, Error on line .* of DoSomething());EXPECT_DEATH / ASSERT_DEATHEXPECT_DEATH(statement,matcher)验证statement使进程以非零退出状态终止且stderr输出匹配matcher。matcher是面向const std::string的 matcher或一个正则表达式语法见 Regular Expression Syntax。注意不带 matcher 的裸字符串s会被当作ContainsRegex(s)而不是Eq(s)——这是使用中最容易踩坑的一点。例如验证调用DoSomething(42)会让进程带着包含My error的错误消息死亡EXPECT_DEATH(DoSomething(42), My error);EXPECT_DEATH_IF_SUPPORTED / ASSERT_DEATH_IF_SUPPORTEDEXPECT_DEATH_IF_SUPPORTED(statement,matcher)如果平台支持 death test行为与EXPECT_DEATH相同否则不做任何验证。EXPECT_DEBUG_DEATH / ASSERT_DEBUG_DEATHEXPECT_DEBUG_DEATH(statement,matcher)调试模式下行为与EXPECT_DEATH相同非调试模式定义了NDEBUG下仅执行statement本身。EXPECT_EXIT / ASSERT_EXITEXPECT_EXIT(statement,predicate,matcher)验证statement导致进程以满足predicate的退出状态终止且stderr输出匹配matcher。predicate是接受int退出状态并返回bool的函数或仿函数。GoogleTest 提供了两个常见用例的谓词// 若程序以给定退出码正常退出则返回 true。 ::testing::ExitedWithCode(exit_code); // 若程序被给定信号杀死则返回 true。 // Windows 上不可用。 ::testing::KilledBySignal(signal_number);matcher的语义与EXPECT_DEATH相同可以是const std::string的 matcher或正则表达式裸字符串按ContainsRegex(s)处理而非Eq(s)。例如验证调用NormalExit()会在stderr打印包含Success的消息并以退出码 0 结束EXPECT_EXIT(NormalExit(), testing::ExitedWithCode(0), Success);仓库中的 gtest-death-test.cc 实现了上述全部 death test 机制配套的 googletest-death-test-test.cc 则覆盖了fast/threadsafe两种风格下的行为可作为深入阅读的实现入口。速查总结场景首选宏布尔表达式EXPECT_TRUE/EXPECT_FALSE直接报失败控制流驱动FAIL()致命/ADD_FAILURE()非致命/ADD_FAILURE_AT数值相等/不等/大小EXPECT_EQ、EXPECT_NE、EXPECT_LT/LE/GT/GEC 字符串按内容比较EXPECT_STREQ/STRNE/STRCASEEQ/STRCASENE浮点近似EXPECT_FLOAT_EQ、EXPECT_DOUBLE_EQ4 ULP、EXPECT_NEAR自定义绝对误差抛/不抛异常EXPECT_THROW、EXPECT_ANY_THROW、EXPECT_NO_THROW多参数自定义谓词EXPECT_PRED1~5、EXPECT_PRED_FORMAT1~5Windows COM HRESULTEXPECT_HRESULT_SUCCEEDED/EXPECT_HRESULT_FAILED进程终止EXPECT_DEATH、EXPECT_EXIT、EXPECT_DEBUG_DEATH、EXPECT_DEATH_IF_SUPPORTEDMatcher 风格断言EXPECT_THAT搭配 Matchers Reference选择EXPECT_还是ASSERT_的核心判据只有一条失败后是否还需要继续执行当前函数的后续断言。前者非致命、继续运行后者致命、立即离开当前函数因此仅能用于void函数。【免费下载链接】googletestGoogleTest - Google Testing and Mocking Framework项目地址: https://gitcode.com/GitHub_Trending/go/googletest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考