• 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 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_handlingEvent.html

    转载请注明出处:http://www.cnblogs.com/jycboy/p/gtest_AdvancedGuide.html

  • 相关阅读:
    《需求工程-软件建模与分析之读书笔记之五》
    Neo4j (3.3.9)的学习之路(1)
    大数据培训第一天总结
    京东B2B业务架构演变阅读心得
    小米网抢购系统开发实践阅读心得
    余额宝技术架构及演进阅读心得
    美图数据统计分析平台架构演进阅读心得
    荔枝架构实践与演进历程阅读心得
    去哪儿网支付系统架构演进全历程阅读心得
    基于SOA质量属性的系统构架分析与实践
  • 原文地址:https://www.cnblogs.com/jycboy/p/gtest_AdvancedGuide.html
Copyright © 2020-2023  润新知