• 玩转Google开源C++单元测试框架Google Test系列(gtest)之五 死亡测试


    一、前言

    “死亡测试”名字比较恐怖,这里的“死亡”指的的是程序的崩溃。通常在测试过程中,我们需要考虑各种各样的输入,有的输入可能直接导致程序崩溃,这时我们就需要检查程序是否按照预期的方式挂掉,这也就是所谓的“死亡测试”。gtest的死亡测试能做到在一个安全的环境下执行崩溃的测试案例,同时又对崩溃结果进行验证。

    二、使用的宏

    Fatal assertion Nonfatal assertion Verifies
    ASSERT_DEATH(statement, regex`); EXPECT_DEATH(statement, regex`); statement crashes with the given error
    ASSERT_EXIT(statement, predicate, regex`); EXPECT_EXIT(statement, predicate, regex`); statement exits with the given error and its exit code matches predicate

    由于有些异常只在Debug下抛出,因此还提供了*_DEBUG_DEATH,用来处理Debug和Realease下的不同。

    三、*_DEATH(statement, regex`)

    1. statement是被测试的代码语句

    2. regex是一个正则表达式,用来匹配异常时在stderr中输出的内容

    如下面的例子:

    void Foo()
    {
        
    int *pInt = 0;
        
    *pInt = 42 ;
    }

    TEST(FooDeathTest, Demo)
    {
        EXPECT_DEATH(Foo(), 
    "");
    }

    重要:编写死亡测试案例时,TEST的第一个参数,即testcase_name,请使用DeathTest后缀。原因是gtest会优先运行死亡测试案例,应该是为线程安全考虑。

    四、*_EXIT(statement, predicate, regex`)

    1. statement是被测试的代码语句

    2. predicate 在这里必须是一个委托,接收int型参数,并返回bool。只有当返回值为true时,死亡测试案例才算通过。gtest提供了一些常用的predicate:

    testing::ExitedWithCode(exit_code)


    如果程序正常退出并且退出码与exit_code相同则返回 true

    testing::KilledBySignal(signal_number)  // Windows下不支持

     
    如果程序被signal_number信号kill的话就返回true

    3. regex是一个正则表达式,用来匹配异常时在stderr中输出的内容

    这里, 要说明的是,*_DEATH其实是对*_EXIT进行的一次包装,*_DEATH的predicate判断进程是否以非0退出码退出或被一个信号杀死。

    例子:

    TEST(ExitDeathTest, Demo)
    {
        EXPECT_EXIT(_exit(
    1),  testing::ExitedWithCode(1),  "");
    }

    五、*_DEBUG_DEATH

    先来看定义:

    #ifdef NDEBUG

    #define EXPECT_DEBUG_DEATH(statement, regex) \
      
    do { statement; } while (false)

    #define ASSERT_DEBUG_DEATH(statement, regex) \
      
    do { statement; } while (false)

    #else

    #define EXPECT_DEBUG_DEATH(statement, regex) \
      EXPECT_DEATH(statement, regex)

    #define ASSERT_DEBUG_DEATH(statement, regex) \
      ASSERT_DEATH(statement, regex)

    #endif  // NDEBUG for EXPECT_DEBUG_DEATH

    可以看到,在Debug版和Release版本下, *_DEBUG_DEATH的定义不一样。因为很多异常只会在Debug版本下抛出,而在Realease版本下不会抛出,所以针对Debug和Release分别做了不同的处理。看gtest里自带的例子就明白了:

    int DieInDebugElse12(int* sideeffect) {
        
    if (sideeffect) *sideeffect = 12;
    #ifndef NDEBUG
        GTEST_LOG_(FATAL, 
    "debug death inside DieInDebugElse12()");
    #endif  // NDEBUG
        
    return 12;
    }

    TEST(TestCase, TestDieOr12WorksInDgbAndOpt)
    {
        
    int sideeffect = 0;
        
    // Only asserts in dbg.
        EXPECT_DEBUG_DEATH(DieInDebugElse12(&sideeffect), "death");

        #ifdef NDEBUG
        
    // opt-mode has sideeffect visible.
        EXPECT_EQ(12, sideeffect);
        
    #else
        
    // dbg-mode no visible sideeffect.
        EXPECT_EQ(0, sideeffect);
        
    #endif
    }

    六、关于正则表达式

    POSIX系统(Linux, Cygwin, 和 Mac中,gtest的死亡测试中使用的是POSIX风格的正则表达式,想了解POSIX风格表达式可参考:

    1. POSIX extended regular expression

    2. Wikipedia entry.

    在Windows系统中,gtest的死亡测试中使用的是gtest自己实现的简单的正则表达式语法。 相比POSIX风格,gtest的简单正则表达式少了很多内容,比如 ("x|y"), ("(xy)"), ("[xy]") 和("x{5,7}")都不支持。

    下面是简单正则表达式支持的一些内容:


    matches any literal character c
    \\d matches any decimal digit
    \\D matches any character that's not a decimal digit
    \\f matches \f
    \\n matches \n
    \\r matches \r
    \\s matches any ASCII whitespace, including \n
    \\S matches any character that's not a whitespace
    \\t matches \t
    \\v matches \v
    \\w matches any letter, _, or decimal digit
    \\W matches any character that \\w doesn't match
    \\c matches any literal character c, which must be a punctuation
    . matches any single character except \n
    A? matches 0 or 1 occurrences of A
    A* matches 0 or many occurrences of A
    A+ matches 1 or many occurrences of A
    ^ matches the beginning of a string (not that of each line)
    $ matches the end of a string (not that of each line)
    xy matches x followed by y

    gtest定义两个宏,用来表示当前系统支持哪套正则表达式风格:

    1. POSIX风格:GTEST_USES_POSIX_RE = 1

    2. Simple风格:GTEST_USES_SIMPLE_RE=1

    七、死亡测试运行方式

    1. fast方式(默认的方式)

    testing::FLAGS_gtest_death_test_style = "fast";

    2. threadsafe方式

    testing::FLAGS_gtest_death_test_style = "threadsafe";


    你可以在 main() 里为所有的死亡测试设置测试形式,也可以为某次测试单独设置。Google Test会在每次测试之前保存这个标记并在测试完成后恢复,所以你不需要去管这部分工作 。如:

    TEST(MyDeathTest, TestOne) {
      testing::FLAGS_gtest_death_test_style 
    = "threadsafe";
      
    // This test is run in the "threadsafe" style:
      ASSERT_DEATH(ThisShouldDie(), "");
    }

    TEST(MyDeathTest, TestTwo) {
      
    // This test is run in the "fast" style:
      ASSERT_DEATH(ThisShouldDie(), "");
    }

    int main(int argc, char** argv) {
      testing::InitGoogleTest(
    &argc, argv);
      testing::FLAGS_gtest_death_test_style 
    = "fast";
      
    return RUN_ALL_TESTS();
    }

    八、注意事项

    1. 不要在死亡测试里释放内存。

    2. 在父进程里再次释放内存。

    3. 不要在程序中使用内存堆检查。

    九、总结

    关于死亡测试,gtest官方的文档已经很详细了,同时在源码中也有大量的示例。如想了解更多的请参考官方的文档,或是直接看gtest源码。

    简单来说,通过*_DEATH(statement, regex`)和*_EXIT(statement, predicate, regex`),我们可以非常方便的编写导致崩溃的测试案例,并且在不影响其他案例执行的情况下,对崩溃案例的结果进行检查。

    系列链接:

    1.玩转Google开源C++单元测试框架Google Test系列(gtest)之一 - 初识gtest

    2.玩转Google开源C++单元测试框架Google Test系列(gtest)之二 - 断言

    3.玩转Google开源C++单元测试框架Google Test系列(gtest)之三 - 事件机制

    4.玩转Google开源C++单元测试框架Google Test系列(gtest)之四 - 参数化

    5.玩转Google开源C++单元测试框架Google Test系列(gtest)之五 - 死亡测试

    6.玩转Google开源C++单元测试框架Google Test系列(gtest)之六 - 运行参数

    7.玩转Google开源C++单元测试框架Google Test系列(gtest)之七 - 深入解析gtest

    8.玩转Google开源C++单元测试框架Google Test系列(gtest)之八 - 打造自己的单元测试框架
  • 相关阅读:
    JFinal 数据库“手动”事务(提交、回滚)
    redis有序集合性能 列表、集合、有序集合
    比特币的原理
    word2vec原理总结
    xgboost 算法总结
    GBDT学习笔记
    LR 算法总结--斯坦福大学机器学习公开课学习笔记
    sklearn的基本使用
    批量梯度下降(BGD)、随机梯度下降(SGD)以及小批量梯度下降(MBGD)的理解
    Logistic回归计算过程的推导
  • 原文地址:https://www.cnblogs.com/coderzh/p/1432043.html
Copyright © 2020-2023  润新知