码迷,mamicode.com
首页 > 编程语言 > 详细

Google C++单元测试框架---AdvancedGuide(译文)上

时间:2016-11-27 20:14:35      阅读:366      评论:0      收藏:0      [点我收藏+]

标签:转换   std   重要   correct   这不   浮点   有用   不同的   path   

本文是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 assertionNonfatal assertionVerifies
ASSERT_THROW(statementexception_type); EXPECT_THROW(statementexception_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 assertionNonfatal assertionVerifies
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 assertionNonfatal assertionVerifies
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 assertionNonfatal assertionVerifies
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 assertionNonfatal assertionVerifies
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 assertionNonfatal assertionVerifies
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_AdvancedGuide.html

 

Google C++单元测试框架---AdvancedGuide(译文)上

标签:转换   std   重要   correct   这不   浮点   有用   不同的   path   

原文地址:http://www.cnblogs.com/jycboy/p/gtest_AdvancedGuide.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!