- 明确的成功与失败
- 断言异常
- Predicate Assertions,以获取更好的错误消息
- 浮点比较
- Asserting 使用 gMock 匹配器
- 更多的字符串断言
- Windows HRESULT assertions
- Type 断言
- Assertion Placement
本节介绍一些不常用但仍然很重要的断言。
# 明确的成功与失败
这三个断言 实际上并不测试值或表达式。相反,它们直接产生成功或失败。像实际执行测试的宏一样,您可以将自定义失败消息流式传输到它们中。
SUCCEED();
产生成功。这不会使整体测试成功。仅当测试在执行期间没有任何断言失败时,该测试才被视为成功。
注意:SUCCEED()
纯粹是记录性的,当前不会生成任何用户可见的输出。但是,将来我们可能会将 SUCCEED()
消息添加到 googletest 的输出中。
FAIL();
ADD_FAILURE();
ADD_FAILURE_AT("file_path", line_number);
2
3
FAIL()
产生致命故障,而 ADD_FAILURE()
和 ADD_FAILURE_AT()
产生非致命故障。当控制流而不是布尔表达式决定测试的成功或失败时,这些选项很有用。例如,您可能想要编写如下内容:
switch(expression) {
case 1:
... some checks ...
case 2:
... some other checks ...
default:
FAIL() << "We shouldn't get here.";
}
2
3
4
5
6
7
8
注意:您只能在返回 void 的函数中使用 FAIL()
。有关更多信息,请参见 Assertion Placement section。
# 断言异常
这些用于验证一段代码是否抛出(或不抛出)给定类型的异常:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_THROW(statement, exception_type); | EXPECT_THROW(statement, exception_type); | statement throws an exception of the given type |
ASSERT_ANY_THROW(statement); | EXPECT_ANY_THROW(statement); | statement throws an exception of any type |
ASSERT_NO_THROW(statement); | EXPECT_NO_THROW(statement); | statement doesn't throw any exception |
例如:
ASSERT_THROW(Foo(5), bar_exception);
EXPECT_NO_THROW({
int n = 5;
Bar(&n);
});
2
3
4
5
6
可用性:Linux,Windows,Mac。
# Predicate Assertions,以获取更好的错误消息
即使 googletest 拥有丰富的断言集,它们也永远无法,也不可能(也不是一个好主意)预测用户可能遇到的所有情况。因此,有时由于缺少更好的宏,用户必须使用 EXPECT_TRUE()
来检查复杂的表达式。这样做的问题是无法向您显示表达式各部分的值,从而很难理解出了什么问题。解决方法是,一些用户选择自己构造故障消息,并将其流式传输到 EXPECT_TRUE()
中。但是,这很尴尬,尤其是当表达式具有副作用或评估开销很高时。
googletest 为您提供了三种不同的解决方案来解决此问题:
# 使用现有的布尔函数
如果您已经具有返回 bool
(或可以隐式转换为 bool
的类型)的函数或函子,则可以在 predicate assertion
中使用它来自由打印函数参数:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_PRED1(pred1, val1) | EXPECT_PRED1(pred1, val1) | pred1(val1) is true |
ASSERT_PRED2(pred2, val1, val2) | EXPECT_PRED2(pred2, val1, val2) | pred2(val1, val2) is true |
... | ... | ... |
在上面,predn
是 n
元 predicate 函数或函子,其中 val1
,val2
,... 和 valn
是其参数。如果 predicate 在应用于给定参数时返回 true
,则 assertion 成功,否则失败。assertion 失败时,它将打印每个参数的值。在这两种情况下,参数均仅被评估一次。
这是一个例子。给定
// Returns true if m and n have no common divisors except 1.
bool MutuallyPrime(int m, int n) { ... }
const int a = 3;
const int b = 4;
const int c = 10;
2
3
4
5
6
assertion
EXPECT_PRED2(MutuallyPrime, a, b);
将成功,而 assertion
EXPECT_PRED2(MutuallyPrime, b, c);
将失败,并显示以下消息
MutuallyPrime(b, c) is false, where
b is 4
c is 10
2
3
注意:如果在使用
ASSERT_PRED*
或EXPECT_PRED*
时看到编译器错误"no matching function to call"("没有匹配的函数要调用"),请参见 此内容 以解决问题。
# 使用返回 AssertionResult 的函数
尽管 EXPECT_PRED*()
和友类可以快速完成工作,但是语法并不令人满意:您必须为不同的 Arities 使用不同的宏,并且感觉起来更像 Lisp,而不是 C++。::testing::AssertionResult
类解决了此问题。
AssertionResult
对象表示 assertion 的结果(无论是成功还是失败,以及相关的消息)。您可以使用以下函数之一创建 AssertionResult
:
namespace testing {
// Returns an AssertionResult object to indicate that an assertion has
// succeeded.
AssertionResult AssertionSuccess();
// Returns an AssertionResult object to indicate that an assertion has
// failed.
AssertionResult AssertionFailure();
}
2
3
4
5
6
7
8
9
10
11
然后,您可以使用 <<
操作符将消息流式传输到 AssertionResult 对象。
要在布尔断言中提供更具可读性的消息(例如 EXPECT_TRUE()
),请编写一个返回 AssertionResult
而不是 bool
的 predicate 函数。例如,如果将 IsEven()
定义为:
::testing::AssertionResult IsEven(int n) {
if ((n % 2) == 0)
return ::testing::AssertionSuccess();
else
return ::testing::AssertionFailure() << n << " is odd";
}
2
3
4
5
6
代替:
bool IsEven(int n) {
return (n % 2) == 0;
}
2
3
失败的断言 assertion EXPECT_TRUE(IsEven(Fib(4)))
将打印:
Value of: IsEven(Fib(4))
Actual: false (3 is odd)
Expected: true
2
3
而不是更模糊的
Value of: IsEven(Fib(4))
Actual: false
Expected: true
2
3
如果您还希望 EXPEX_FALSE
和 ASSERT_FALSE
中提供信息性消息(Google 代码库中三分之一的布尔断言是否定的),并且可以在成功情况下使 predicate 变慢,则可以提供成功消息:
:testing::AssertionResult IsEven(int n) {
if ((n % 2) == 0)
return ::testing::AssertionSuccess() << n << " is even";
else
return ::testing::AssertionFailure() << n << " is odd";
}
2
3
4
5
6
然后将显示语句 EXPECT_FALSE(IsEven(Fib(6)))
Value of: IsEven(Fib(6))
Actual: true (8 is even)
Expected: false
2
3
# 使用 Predicate-Formatter
如果您发现 (ASSERT|EXPECT)_PRED*
和 (ASSERT|EXPECT)_(TRUE|FALSE)
生成的默认消息不令人满意,或者 predicate 的某些参数不支持向 ostream
进行流传输,则可以使用以下 predicate-formatter
断言,以完全自定义消息的格式:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_PRED_FORMAT1(pred_format1, val1); | EXPECT_PRED_FORMAT1(pred_format1, val1); | pred_format1(val1) is successful |
ASSERT_PRED_FORMAT2(pred_format2, val1, val2); | EXPECT_PRED_FORMAT2(pred_format2, val1, val2); | pred_format2(val1, val2) is successful |
... | ... | ... |
此宏与上一组宏之间的区别在于,(ASSERT|EXPECT)_PRED_FORMAT*
代替 predicate,而带有 predicate-formatter 程序(pred_formatn
),后者是具有信号的函数或函子:
::testing::AssertionResult PredicateFormattern(const char* expr1,
const char* expr2,
...
const char* exprn,
T1 val1,
T2 val2,
...
Tn valn);
2
3
4
5
6
7
8
其中 val1
,val2
,... 和 valn
是 predicate 参数的值,而 expr1
,expr2
,... 和 exprn
是它们在源代码中出现的相应表达式。类型 T1
,T2
,... 和 Tn
可以是值类型或引用类型。例如,如果参数的类型为 Foo
,则可以将其声明为 Foo
或 const Foo&
(以适当者为准)。
例如,让我们改进与 EXPECT_PRED2()
一起使用的 MutuallyPrime()
中的失败消息:
// Returns the smallest prime common divisor of m and n,
// or 1 when m and n are mutually prime.
int SmallestPrimeCommonDivisor(int m, int n) { ... }
// A predicate-formatter for asserting that two integers are mutually prime.
::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);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
有了这个 predicate-formatter ,我们可以使用
EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c);
生成消息
b and c (4 and 10) are not mutually prime, as they have a common divisor 2.
您可能已经意识到,我们前面介绍的许多内置断言都是 (EXPECT|ASSERT)_PRED_FORMAT*
的特殊情况。实际上,大多数确实是使用 (EXPECT|ASSERT)_PRED_FORMAT*
定义的。
# 浮点比较
比较浮点数很棘手。由于舍入误差,两个浮点将很难完全匹配。因此,ASSERT_EQ
的直接比较通常不起作用。并且由于浮点可以具有较宽的值范围,因此没有单个固定的错误界限有效。最好以固定的相对误差范围进行比较,但由于精度损失而使值接近 0 时除外。
通常,为了使浮点比较有意义,用户需要仔细选择误差范围。如果他们不希望或不在乎,则比较"末尾单位项"(ULP)是一个很好的默认值,并且 googletest 提供了断言来做到这一点。有关 ULP 的详细信息很长。如果您想了解更多信息,请参见 此处 (opens new window)。
# 浮点宏
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_FLOAT_EQ(val1, val2); | EXPECT_FLOAT_EQ(val1, val2); | the two float values are almost equal |
ASSERT_DOUBLE_EQ(val1, val2); | EXPECT_DOUBLE_EQ(val1, val2); | the two double values are almost equal |
"几乎相等"是指这些值彼此在 4 个 ULP 之内。
以下断言允许您选择可接受的错误范围:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_NEAR(val1, val2, abs_error); | EXPECT_NEAR(val1, val2, abs_error); | the difference between val1 and val2 doesn't exceed the given absolute error |
# 浮点 Predicate-Format 函数
一些浮点运算很有用,但并不常用。为了避免出现大量新的宏,我们将其作为可用于 predicate assertion 宏(例如 EXPECT_PRED_FORMAT2
等)的 predicate-format 函数提供。
EXPECT_PRED_FORMAT2(::testing::FloatLE, val1, val2);
EXPECT_PRED_FORMAT2(::testing::DoubleLE, val1, val2);
2
验证 val1
小于或几乎等于 val2
。您可以将上表中的 EXPECT_PRED_FORMAT2
替换为 ASSERT_PRED_FORMAT2
。
# Asserting 使用 gMock 匹配器
gMock 带有一个匹配器库,用于验证传递给模拟对象的参数。gMock 匹配器基本上是知道如何描述自己的 predicate。可以在以下 assertion 宏中使用它:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_THAT(value, matcher); | EXPECT_THAT(value, matcher); | value matches matcher |
例如,StartsWith(prefix)
是一个匹配器,它匹配以前缀开头的字符串,您可以编写:
using ::testing::StartsWith;
...
// Verifies that Foo() returns a string starting with "Hello".
EXPECT_THAT(Foo(), StartsWith("Hello"));
2
3
4
有关更多详细信息,请阅读 gMock 中的 此信息。
gMock 具有丰富的匹配器集。您可以做很多 googletest 无法独自完成的事情。有关 gMock 提供的匹配器列表,请阅读 此内容。编写 自己的匹配器 也很容易。
gMock 与 googletest 捆绑在一起,因此您无需添加任何构建依赖项即可利用此优势。只需添加 "testing/base/public/gmock.h"
,即可开始使用。
# 更多的字符串断言
(如果尚未阅读,请先阅读 [上一节](./advanced.md#Asserting%20 使用%20gMock%20 匹配器))
using ::testing::HasSubstr;
using ::testing::MatchesRegex;
...
ASSERT_THAT(foo_string, HasSubstr("needle"));
EXPECT_THAT(bar_string, MatchesRegex("\\w*\\d+"));
2
3
4
5
如果该字符串包含格式正确的 HTML 或 XML 文档,则可以检查其 DOM 树是否与 XPath 表达式匹配:
// Currently still in //template/prototemplate/testing:xpath_matcher
#include "template/prototemplate/testing/xpath_matcher.h"
using prototemplate::testing::MatchesXPath;
EXPECT_THAT(html_string, MatchesXPath("//a[text()='click here']"));
2
3
4
# Windows HRESULT assertions
这些断言测试 HRESULT
是成功还是失败。
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_HRESULT_SUCCEEDED(expression) | EXPECT_HRESULT_SUCCEEDED(expression) | expression is a success HRESULT |
ASSERT_HRESULT_FAILED(expression) | EXPECT_HRESULT_FAILED(expression) | expression is a failure HRESULT |
生成的输出包含 expression
返回的 HRESULT
代码相关的适合人类阅读错误消息。
您可以这样使用它们:
CComPtr<IShellDispatch2> shell;
ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application"));
CComVariant empty;
ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));
2
3
4
# Type 断言
您可以调用该函数
::testing::StaticAssertTypeEq<T1, T2>();
来判断类型 T1
和 T2
是相同的。如果 assertion 得到满足,该函数将不执行任何操作。如果类型不同,则函数调用将无法编译,编译器错误消息将指出 T1 and T2 are not the same type
(T1
和 T2
不是同一类型),并且很可能(取决于编译器)向您显示 T1
和 T2
的实际值。这在模板代码内部主要有用。
警告:在类模板或函数模板的成员函数中使用时,StaticAssertTypeEq<T1, T2>()
仅在函数实例化时才有效。例如,给定:
template <typename T> class Foo {
public:
void Bar() { ::testing::StaticAssertTypeEq<int, T>(); }
};
2
3
4
代码:
void Test1() { Foo<bool> foo; }
不会产生编译器错误,因为 Foo<bool>::Bar()
实际上从未
实例化。相反,您需要:
void Test2() { Foo<bool> foo; foo.Bar(); }
来导致编译器错误。
# Assertion Placement
您可以在任何 C++ 函数中使用 assertion。特别是,它不一定是测试 fixture 类的方法。一个约束是,生成致命故障的断言(FAIL*
和 ASSERT_*
)只能在返回 void 的函数中使用。这是 Google 不使用异常的结果。通过将其放置在非 void 函数中,您将得到一个令人困惑的编译错误,例如:
"error: void value not ignored as it ought to be"
("错误:void 值不应被忽略")"cannot initialize return object of type 'bool' with an rvalue of type 'void'"
("无法使用值为 "void" 的右值初始化类型为 "bool" 的返回对象)"error: no viable conversion from 'void' to 'string'"
("错误:无法从 'void' 转换为 'string'")。
如果需要在返回非空的函数中使用致命断言,则一种选择是使函数返回 out 参数中的值。例如,您可以重写 T2 Foo(T1 x)
为 void Foo(T1 x, T2* result)
。您需要确保 *result
包含一些合理的值,即使函数过早返回也是如此。由于函数现在返回 void,因此可以在其中使用任何 assertion。
如果不能选择更改函数的类型,则应仅使用会产生非致命故障的 assertion,例如ADD_FAILURE*
和 EXPECT_*
。
注意:根据 C++ 语言规范,构造函数和析构函数不被视为返回 void 的函数,因此您不得在其中使用致命的断言;如果尝试,将出现编译错误。相反,要么调用 abort
并崩溃整个可执行测试,要么将致命 assertion 放在 SetUp
/TearDown
函数中。参见 构造函数/析构函数与 SetUp
/TearDown
警告:从构造函数或析构函数调用的辅助函数(私有 void 返回方法)中的致命 assertion 不会终止当前测试,正如您的直觉所暗示的那样:您只是从构造函数或析构函数中早返回,可能会令您的对象处于部分构造或部分破坏的状态!您几乎肯定要 abort
或改用 SetUp
/TearDown。
需注意
生成致命故障的断言(FAIL*
和 ASSERT_*
)只能在返回 void 的函数中使用