背景简介
前篇介绍了GoogleTest框架的几个重要的API宏,支撑起了整个测试执行的流程。本节主要介绍另一类重要的API宏,即测试中使用的断言宏,是测试的具体实现。
前篇: GoogleTest常用宏源码探究(一)
AssertionResult类
这是Gtest断言机制中非常重要的一个类,顾名思义,它表示断言的结果,然而不仅如此,它还包含失败的原因信息。
它有两个私有成员变量,一个布尔变量表示测试的成败,一个字符串指针指向失败原因信息;其公开成员方法有 = , ! , << 等运算符以及拷贝构造函数的重写,messege()方法来获取信息,并且定义了转换运算符bool(),可当做bool来作为if的条件使用。
该类实例的创建通过工厂方法:AssertionSuccess()和AssertionFailure()。这两个函数的用法如下
定义一个判断偶数的函数 |
testing::AssertionResult IsEven(int n) { |
if ((n % 2) == 0) |
return testing::AssertionSuccess(); |
else |
return testing::AssertionFailure() << n << ' is odd'; |
} |
|
我们断言5为偶数:EXPECT_TRUE(IsEven(Fib(5))) |
结果当然为failure,它会显示如下详细信息: |
|
Value of: IsEven(Fib(5)) |
Actual: false (5 is odd) |
Expected: true |
|
而不是这种模糊的信息 |
|
Value of: IsEven(Fib(5)) |
Actual: false |
Expected: true |
|
上面这个例子很简单,只是一个布尔判断。 当我们所处理的情况更为复杂时,这种展示错误原因就显得很重要了。 |
|
如果我们希望无论是正向断言还是负向断言出错时都提供一些信息 (一般当正向断言和负向断言出现几率大致相当时才这么做) |
|
testing::AssertionResult IsEven(int n) { |
if ((n % 2) == 0) |
return testing::AssertionSuccess() << n << ' is even'; |
else |
return testing::AssertionFailure() << n << ' is odd'; |
} |
|
断言Fib(6)是偶数为假,EXPECT_FALSE(IsEven(Fib(6))) 将会显示 |
|
Value of: IsEven(Fib(6)) |
Actual: true (8 is even) |
Expected: false |
|
注意:在正向断言中穿插负向断言会导致效率很低, 因此不要在大量(万以上数量级)正向断言中使用负向断言。 原文: Predicates that support negative Boolean assertions have reduced |
performance in positive ones so be careful not to use them in tests |
that have lots (tens of thousands) of positive Boolean assertions. |
|
在EXPECT_PRED_FORMAT断言中使用该类,例如: |
|
验证某函数Foo() 是否返回一个偶数值 |
EXPECT_PRED_FORMAT1(IsEven, Foo()); |
|
你需要这样定义: |
|
testing::AssertionResult IsEven(const char* expr, int n) { |
if ((n % 2) == 0) |
return testing::AssertionSuccess(); |
else |
return testing::AssertionFailure() |
<< 'Expected: ' << expr << ' is even
Actual: it's ' << n; |
} |
如果Foo()返回5,那么将显示: |
Expected: Foo() is even |
Actual: it's 5 |
ASSERT_XX()、EXPECT_XX()系列宏
ASSERT_XX和EXPECT_XX总是成对出现,区别在于ASSERT_XX失败会中止流程,EXPECT_XX失败流程会继续。
XX在这里可以是:
————————————————————————————————————————————————
THROW,有指定类型的异常抛出
NO_THROW,没有任何异常抛出
ANY_THROW,抛出了任意类型的异常
EQ,相等
NE,不等
LE,小于等于
LT,小于
GE,大于等于
GT,大于
TRUE,为真
False,为假
STREQ,字符串相等
STRNE,字符串不等
STRCASEEQ,字符串相等(不区分大小写)
STRCASENE,字符串不等(不区分大小写)
FLOAT_EQ,浮点数相等
DOUBLE_EQ,双精浮点数相等
NEAR,两个双精浮点数之差在指定范围内
HRESULT_SUCCEEDED,Window数据类型HRESULT值为SUCCEEDED
HRESULT_FAILED,Window数据类型HRESULT值为FAILED
NO_FATAL_FAILURE,当前线程无fatal级错误
————————————————————————————————————————————————
以上这些ASSERT_XX,EXPECT_XX的宏定义在gtest/gtest.h,进行一级展开后可归结为几类,每类以一个定义为例子:
异常类:GTEST_TEST_THROW_,GTEST_TEST_NO_THROW_,GTEST_TEST_ANY_THROW_
#define EXPECT_THROW(statement, expected_exception) GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) |
#define EXPECT_NO_THROW(statement) |
GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) |
#define EXPECT_ANY_THROW(statement) |
GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) |
布尔类:GTEST_TEST_BOOLEAN_
#define EXPECT_TRUE(condition) GTEST_TEST_BOOLEAN_(condition, #condition, false, true, GTEST_NONFATAL_FAILURE_) |
EXPECT数值比较类:EXPECT_PRED_FORMAT2
#define EXPECT_EQ(val1, val2) EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) |
ASSERT数值比较类:ASSERT_PRED_FORMAT2
#define GTEST_ASSERT_EQ(val1, val2) |
ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) |
#if !GTEST_DONT_DEFINE_ASSERT_EQ |
#define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) |
#endif |
EXPECT HRESULT类:EXPECT_PRED_FORMAT1
#define EXPECT_HRESULT_SUCCEEDED(expr) EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) |
ASSETY HRESULT类:ASSERT_PRED_FORMAT1
#define ASSERT_HRESULT_SUCCEEDED(expr) ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) |
致命错误类:GTEST_TEST_NO_FATAL_FAILURE_
#define ASSERT_NO_FATAL_FAILURE(statement) GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) |
这些宏的进一步展开定义在gtest/internal/gtest-internal.h或gtest/gtest_pred_impl.h,下面对这些宏按分类进行展开:
共性:
1. 注意到ASSERT_EQ等宏和EXPECT_EQ等宏的定义方式不同,ASSERT首先定义的是GTEST_ASSERT_EQ宏为ASSERT_PRED_FORMAT2,在ASSERT_EQ无冲突的情况下才定义ASSERT_EQ为GTEST_ASSERT_EQ。因为ASSERT很多时候会在别处也有定义,这时我们还可以使用GTEST_ASSERT_EQ来避免冲突。
2. 另外,ASSERT_XX和EXPECT_XX最终都合并到一个宏,二者主要差别在于在展开的宏中的一个参数值不同,ASSERT该参数值为GTEST_FATAL_FAILURE,而EXPECT该参数值为GTEST_NONFATAL_FAILURE。
GTEST_FATAL_FAILURE和GTEST_NONFATAL_FAILURE用于传递错误信息。其相关定义如下:
#define GTEST_MESSAGE_AT_(file, line, message, result_type) ::testing::internal::AssertHelper(result_type, file, line, message) |
= ::testing::Message() |
|
#define GTEST_MESSAGE_(message, result_type) |
GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) |
|
#define GTEST_FATAL_FAILURE_(message) |
return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) |
|
#define GTEST_NONFATAL_FAILURE_(message) |
GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) |
|
#define GTEST_SUCCESS_(message) |
GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) |
|
#define GTEST_SKIP_(message) |
return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip) |
可以看到二者代表两种不同级别错误,最终都扩展为创建一个::testing::internal::AssertHelper对象,并注册一个新的Message对象(这里=运算符被重写,意义并不是赋值或拷贝)。这两个宏分别在ASSERT和EXPECT断言失败时被调用。
AssertHelper类使得信息可传入断言宏,Message类就像是一个输出流缓存,我们将信息流输入Message存储,然后将Messege传到输出流使得信息流输出。
3.在后续的展开中,都会有GTEST_AMBIGUOUS_ELSE_BLOCKER_这个宏,它的作用是关闭gcc编译器对于ambiguous else的warning。该warning出现当嵌套的if语句后跟随else语句,没有{}来明确else绑定哪个if时。其方式是将其放到switch (0) case 0: default:内,定义如下:
// The GNU compiler emits a warning if nested 'if' statements are followed by // an 'else' statement and braces are not used to explicitly disambiguate the |
// 'else' binding. This leads to problems with code like: |
// |
// if (gate) |
// ASSERT_*(condition) << 'Some message'; |
// |
// The 'switch (0) case 0:' idiom is used to suppress this. |
#ifdef __INTEL_COMPILER |
# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ |
#else |
# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT |
#endif |
异常类断言宏
#define GTEST_TEST_THROW_(statement, expected_exception, fail) GTEST_AMBIGUOUS_ELSE_BLOCKER_ |
if (::testing::internal::ConstCharPtr gtest_msg = '') { |
bool gtest_caught_expected = false; |
try { |
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); |
} |
catch (expected_exception const&) { |
gtest_caught_expected = true; |
} |
catch (...) { |
gtest_msg.value = |
'Expected: ' #statement ' throws an exception of type ' |
#expected_exception '.
Actual: it throws a different type.'; |
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); |
} |
if (!gtest_caught_expected) { |
gtest_msg.value = |
'Expected: ' #statement ' throws an exception of type ' |
#expected_exception '.
Actual: it throws nothing.'; |
goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); |
} |
} else |
GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): |
fail(gtest_msg.value) |
|
#define GTEST_TEST_NO_THROW_(statement, fail) |
GTEST_AMBIGUOUS_ELSE_BLOCKER_ |
if (::testing::internal::AlwaysTrue()) { |
try { |
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); |
} |
catch (...) { |
goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); |
} |
} else |
GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): |
fail('Expected: ' #statement ' doesn't throw an exception.
' |
' Actual: it throws.') |
|
#define GTEST_TEST_ANY_THROW_(statement, fail) |
GTEST_AMBIGUOUS_ELSE_BLOCKER_ |
if (::testing::internal::AlwaysTrue()) { |
bool gtest_caught_any = false; |
try { |
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); |
} |
catch (...) { |
gtest_caught_any = true; |
} |
if (!gtest_caught_any) { |
goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); |
} |
} else |
GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): |
fail('Expected: ' #statement ' throws an exception.
' |
' Actual: it doesn't.') |
上段代码中GTEST_CONCAT_TOKEN_宏是一个跳转标记
#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) #define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar |
可以看到这段代码有些不寻常,if的条件是AlwaysTrue(),该方法在头文件中解释为永远返回true,这将导致else永远不会被执行。然后在if中嵌套一个if,如果真则使用goto跳转到else部分。
为什么使用这种逻辑结构有些令人不解,也许因为AlwaysTrue()的实现暗藏玄机,并非真的永远返回true?或者只是想把else绑定到内嵌的if,出于编译相关的某种原因而这样处理?这个函数定义在internal中,不是开源的,我们无法知晓。
回到代码的主逻辑,可以看到是使用try-catch来捕获异常,try中使用一个宏GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_,该宏展开为:
#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) if (::testing::internal::AlwaysTrue()) { statement; } |
再次出现了if(AlwaysTrue())的用法,官方解释是为了关闭MSVC warning 4072(unreachable code)。
然后整体逻辑就很简单了,在try中执行方法,捕获到异常后,会调用fail方法注册相关失败信息,fail这里是GTEST_FATAL_FAILURE或GTEST_NONFATAL_FAILURE宏,其展开参见前面谈及的共性2。
布尔类断言宏
这类宏最终展开是一个if-else语句,将表达式的结果(bool型)转为gtest的类型AssertionResult,当其不为true时,调用fail方法注册相关失败信息。
expression在这里是一个由前面传进来的函数表达式,这类函数主要完成比较运算,返回一个AssertionResult对象。
#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) |
GTEST_AMBIGUOUS_ELSE_BLOCKER_ |
if (const ::testing::AssertionResult gtest_ar_ = |
::testing::AssertionResult(expression)) |
; |
else |
fail(::testing::internal::GetBoolAssertionFailureMessage( |
gtest_ar_, text, #actual, #expected).c_str()) |
数值比较类断言宏
这类宏无论是ASSERT_XX还是EXPECT_XX最终都展为GTEST_ASSERT_,而GTEST_ASSERT_展为一个if-else语句,同上。
#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure) GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), |
on_failure) |
|
#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) |
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) |
#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) |
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) |
#define GTEST_ASSERT_(expression, on_failure) GTEST_AMBIGUOUS_ELSE_BLOCKER_ |
if (const ::testing::AssertionResult gtest_ar = (expression)) |
; |
else |
on_failure(gtest_ar.failure_message()) |
HRESULT类断言宏
这类宏也都转为GTEST_ASSERT_(),同上
#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure) GTEST_ASSERT_(pred_format(#v1, v1), |
on_failure) |
|
#define EXPECT_PRED_FORMAT1(pred_format, v1) |
GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) |
#define ASSERT_PRED_FORMAT1(pred_format, v1) |
GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) |
致命错误类断言宏
该宏用于断言一个函数过程中不发生致命错误。其逻辑结构和上面的类似,并且也出现了if-goto-else的结构。
#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) GTEST_AMBIGUOUS_ELSE_BLOCKER_ |
if (::testing::internal::AlwaysTrue()) { |
::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; |
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); |
if (gtest_fatal_failure_checker.has_new_fatal_failure()) { |
goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); |
} |
} else |
GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): |
fail('Expected: ' #statement ' doesn't generate new fatal ' |
'failures in the current thread.
' |
' Actual: it does.') |
SUCCEEDED()、FAILURE()系列宏
上一节的ASSERT、EXPECT宏通过一个判断过程来得出AssertionResult,而本节的这些宏将直接指定AssertionResult,用于我们自定义更复杂的测试结果判断方法。
这类宏具体有以下:
————————————————————————————————————————————————
ADD_FAILURE() 产生一个非致命错误,附带通用信息“Failed”
ADD_FAILURE_AT() 同上,并额外指明所在文件和行数
GTEST_FAIL() 产生一个致命错误,附带通用信息“Failed”
GTEST_FAIL_AT() 同上,并额外指明所在文件和行数
FAIL() 同GTEST_FAIL()
GTEST_SUCCEED() 产生一个成功结果,附带通用信息“Succeeded”
SUCCEED() 同GTEST_SUCCEED()
————————————————————————————————————————————————
这类宏最终都展开为GTEST_MESSAGE_AT_,和ASSERT、EXPECT的if语句的条件内容是一样的,参见前面的共性2。
#define GTEST_MESSAGE_AT_(file, line, message, result_type) ::testing::internal::AssertHelper(result_type, file, line, message) |
= ::testing::Message() |
____________________________________________________
BY:HW-HH