Google C++单元测试框架GoogleTest---AdvancedGuide(译文)上
本文是gtest高级测试指南的译文,由于文章太长,分上下两部分。
一、简介
本文档将向您展示更多的断言,以及如何构造复杂的失败消息,传播致命的故障,重用和加速您的测试夹具,并在您的测试使用各种标志。
二、更多断言
本节包括一些不太常用,但仍然重要的断言。
2.1 显式成功和失败
这三个断言实际上不测试值或表达式。 相反,它们直接产生成功或失败。 与实际执行测试的宏类似,您可以将自定义失败消息流入它们。
SUCCEED();
生成成功。 这不会使整体测试成功。 只有当测试在其执行期间没有任何断言失败时,测试才被认为是成功的。
注意:SUCCEED()是纯纪录片,目前不生成任何用户可见的输出。 但是,我们可能会在未来向Google Test的输出中添加SUCCEED()消息。
FAIL(); ADD_FAILURE(); ADD_FAILURE_AT("file_path",line_number);
FAIL()产生致命故障,而ADD_FAILURE()和ADD_FAILURE_AT()产生非致命故障。 当控制流而不是布尔表达式确定测试的成功或失败时,这些是有用的。 例如,您可能想要写如下:
switch(expression) { case 1: ... some checks ... case 2: ... some other checks ... default: FAIL() << "We shouldn't get here."; }
注意:你只能在返回void的函数中使用FAIL()。 有关详细信息,请参阅 Assertion Placement section 部分。
2.2 异常断言
这些用于验证一段代码抛出(或不抛出)给定类型的异常:
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 |
Examples:
ASSERT_THROW(Foo(5), bar_exception); EXPECT_NO_THROW({ int n = 5; Bar(&n); });
三、更好的错误消息的谓词断言
虽然Google测试有一套丰富的断言,但它们永远不可能完整,因为它不可能(也不是一个好主意)预测用户可能遇到的所有情况。 因此,有时用户必须使用EXPECT_TRUE()来检查复杂表达式,因为缺少更好的宏。 这有一个问题,没有显示你的表达式的部分的值,使得很难理解什么错误。 作为解决方法,一些用户选择自己构造失败消息,将其流式传输到EXPECT_TRUE()。 然而,这是尴尬,特别是当表达式有副作用或评价昂贵。
Google测试提供三种不同的选项来解决这个问题:
3.1使用现有的布尔函数
如果你已经有一个函数或函数返回bool(或一个可以隐式转换为bool的类型),你可以在谓词断言中使用它来获得免费打印的函数参数:
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_PRED1( pred1, val1); |
EXPECT_PRED1( pred1, val1); |
pred1(val1) returns true |
ASSERT_PRED2( pred2, val1, val2); |
EXPECT_PRED2( pred2, val1, val2); |
pred2(val1, val2) returns true |
... | ... | ... |
在上面,predn是一个n元谓词函数或函子,其中val1,val2,...和valn是它的参数。 如果谓词在应用于给定参数时返回true,则断言成功,否则失败。 当断言失败时,它打印每个参数的值。 在任何一种情况下,参数只计算一次。
Here's an example:
// Returns true iff 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;
断言EXPECT_PRED2(Mutual Prime,a,b); 将成功,而断言EXPECT_PRED2(MutuallyPrime,b,c); 将失败。
!MutuallyPrime(b, c) is false, where b is 4 c is 10
注意:
1. 如果在使用ASSERT_PRED *或EXPECT_PRED *时看到编译器错误“no matching function to call(无匹配函数调用)”,请参阅此常见问题解答 this FAQ 以了解如何解决它。
2. 目前我们只提供arity <= 5的谓词断言。如果你需要更高级的断言,让我们知道。
3.2 使用返回AssertionResult的函数
虽然EXPECT_PRED *()和friends对快速工作很方便,但是语法不令人满意:你必须使用不同的宏不同的arities,它感觉更像Lisp而不是C ++。 :: testing :: AssertionResult类解决了这个问题。
AssertionResult对象表示断言的结果(无论它是成功还是失败,以及相关联的消息)。 您可以使用以下工厂函数之一创建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(); }
然后,您可以使用<<运算符将消息流式传输到AssertionResult对象。
要在布尔断言(例如EXPECT_TRUE())中提供更多可读消息,请编写一个返回AssertionResult而不是bool的谓词函数。 例如,如果您将IsEven()定义为:
::testing::AssertionResult IsEven(int n) { if ((n % 2) == 0) return ::testing::AssertionSuccess(); else return ::testing::AssertionFailure() << n << " is odd"; }
而不是:
bool IsEven(int n) { return (n % 2) == 0; }
the failed assertion EXPECT_TRUE(IsEven(Fib(4)))
will print:
Value of: IsEven(Fib(4)) Actual: false (*3 is odd*) Expected: true
instead of a more opaque:
Value of: IsEven(Fib(4)) Actual: false Expected: true
如果您希望在EXPECT FALSE和ASSERT_FALSE中看到提供信息的消息,并且在成功的情况下使谓词变慢,您可以提供一个成功消息:
::testing::AssertionResult IsEven(int n) { if ((n % 2) == 0) return ::testing::AssertionSuccess() << n << " is even"; else return ::testing::AssertionFailure() << n << " is odd"; }
Then the statement EXPECT_FALSE(IsEven(Fib(6)))
will print
Value of: IsEven(Fib(6)) Actual: true (8 is even) Expected: false
3.3 使用谓词格式化
如果你发现由(ASSERT | EXPECT)_PRED *和(ASSERT | EXPECT)_(TRUE | FALSE)生成的默认消息不令人满意,或者您的谓词的某些参数不支持流到ostream,您可以使用以下谓词 - 格式化程序断言 以完全自定义消息的格式化:
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 *采用谓词格式化器(pred_formatn),它是一个函数或函数签名:
::testing::AssertionResult PredicateFormattern(const char*expr1, const char*expr2, ... const char*exprn, T1val1, T2val2, ... Tnvaln);
四、浮点比较
比较浮点数是棘手的。 由于舍入误差,两个浮点不太可能完全匹配。 因此,ASSERT_EQ的幼稚比较通常不起作用。 并且由于浮点可以具有宽的值范围,没有单个固定误差界限工作。 最好通过固定的相对误差界限进行比较,除了接近0的值由于精度的损失。
一般来说,对于浮点比较有意义,用户需要仔细选择误差界限。 如果他们不想要或关心,根据最后地点(ULP)中的单位进行比较是一个很好的默认值,Google测试提供了断言来做到这一点。 关于ULP的完整详细信息相当长; 如果你想了解更多,请参阅这篇关于浮动比较的文章 this article on float comparison.。
Floating-Point Macros
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 |
。。。。太多,需要时再去看。
五、Windows HRESULT断言
这些断言测试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代码相关联的人工可读错误消息。
You might use them like this:
CComPtr shell; ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application")); CComVariant empty; ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));
六、类型断言
::testing::StaticAssertTypeEq<T1, T2>();
您可以调用该函数,来声称断言类型T1和T2是相同的。 如果满足断言,该函数不执行任何操作。 如果类型不同,函数调用将无法编译,编译器错误消息(取决于编译器)将显示T1和T2的实际值。 这主要在模板代码中有用。
注意:当在类模板或函数模板的成员函数中使用时,StaticAssertTypeEq <T1,T2>()仅在函数实例化时有效。 例如,给定:
template <typename T> class Foo { public: void Bar() { ::testing::StaticAssertTypeEq<int, T>(); } };
the code:
void Test1() { Foo<bool> foo; }
将不会生成编译器错误,因为Foo <bool> :: Bar()永远不会实际实例化。 相反,您需要:
void Test2() { Foo<bool> foo; foo.Bar(); }
导致编译器错误。
七、Assertion Placement(断言放置)
你可以在任何C ++函数中使用断言。 特别地,它不必是测试夹具类的方法。 一个约束是生成致命故障(FAIL *和ASSERT_ *)的断言只能在void返回函数中使用。 这是Google测试不使用exceptions的后果。 如果将它放在一个非void函数中,你会得到一个令人困惑的编译错误,如“error: void value not ignored as it ought to be”。
如果需要在返回非void的函数中使用断言,一个选项是使函数返回out参数中的值。 例如,您可以将T2 Foo(T1 x)重写为void Foo(T1 x,T2 * result)。 你需要确保* result包含一些合理的值,即使该函数过早返回。 由于函数现在返回void,你可以在它里面使用任何断言。
如果更改函数的类型不是一个选项,则应该使用生成非致命失败的断言,例如ADD_FAILURE *和EXPECT_ *。
注意:根据C ++语言规范,构造函数和析构函数不被视为void返回函数,因此您不能在其中使用致命断言。 如果你尝试,你会得到一个编译错误。 一个简单的解决方法是将构造函数或析构函数的整个体转移到私有void返回方法。 然而,你应该意识到,构造函数中的致命断言失败并不会终止当前的测试,正如你的直觉所暗示的那样; 它只是从构造函数早期返回,可能使您的对象处于部分构造状态。 同样,析构函数中的致命断言失败可能使您的对象处于部分破坏状态。 在这些情况下仔细使用断言!
八、教学Google测试如何打印您的值
当测试声明(如EXPECT_EQ)失败时,Google Test会打印参数值以帮助您调试。 它使用用户可扩展值打印机。
此打印机知道如何打印内置的C ++类型,native数组,STL容器和任何支持<<运算符的类型。 对于其他类型,它打印值中的原始字节,并希望用户可以计算出来。
如前所述,打印机是可扩展的。 这意味着你可以教它做一个更好的工作,打印你的特定类型,而不是转储字节。 要做到这一点,定义<<您的类型:
#include <iostream> namespace foo { class Bar { ... }; // We want Google Test to be able to print instances of this. // It's important that the << operator is defined in the SAME // namespace that defines Bar. C++'s look-up rules rely on that. ::std::ostream& operator<<(::std::ostream& os, const Bar& bar) { return os << bar.DebugString(); // whatever needed to print bar to os } } // namespace foo
有时,这可能不是一个选项:你的团队可能认为它的坏风格有一个<<运算符的Bar,或者Bar可能已经有一个<<运算符,不做你想要的(你不能改变它)。 如果是这样,您可以定义一个PrintTo()函数,如下所示:
#include <iostream> namespace foo { class Bar { ... }; // It's important that PrintTo() is defined in the SAME // namespace that defines Bar. C++'s look-up rules rely on that. void PrintTo(const Bar& bar, ::std::ostream* os) { *os << bar.DebugString(); // whatever needed to print bar to os } } // namespace foo
如果您定义了<<和PrintTo(),后者将在Google测试时使用。 这允许您自定义值如何显示在Google测试的输出中,而不影响依赖于其<<运算符的行为的代码。
如果你想使用Google Test的值打印机自己打印一个值x,只需调用:: testing :: PrintToString(x),它返回一个std :: string:
vector<pair<Bar, int> > bar_ints = GetBarIntVector(); EXPECT_TRUE(IsCorrectBarIntVector(bar_ints)) << "bar_ints = " << ::testing::PrintToString(bar_ints);
Extending Google Test by Handling Test Events
这个挺复杂,写在单独的文档中: http://www.cnblogs.com/jycboy/p/gtest_handlingEvent.html
转载请注明出处:http://www.cnblogs.com/jycboy/p/gtest_AdvancedGuide.html