文章目录
一、前言
“死亡测试”名字比较恐怖,这里的“死亡”指的的是程序的崩溃。通常在测试过程中,我们需要考虑各种各样的输入,有的输入可能直接导致程序崩溃,这时我们就需要检查程序是否按照预期的方式挂掉,这也就是所谓的“死亡测试”。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 |
三、*_DEATH(statement, regex)
-
statement是被测试的代码语句
-
regex是一个正则表达式,用来匹配异常时在stderr中输出的内容
如下面的例子:
void Foo()
{
int *pInt = 0;
*pInt = 42 ;
}
TEST(FooDeathTest, Demo)
{
EXPECT_DEATH(Foo(), "");
}
重要:编写死亡测试案例时,TEST的第一个参数,即testcase_name,请使用DeathTest后缀。原因是gtest会优先运行死亡测试案例,应该是为线程安全考虑。
四、*_EXIT(statement, predicate, regex`)
- statement是被测试的代码语句
- predicate 在这里必须是一个委托,接收int型参数,并返回bool。只有当返回值为true时,死亡测试案例才算通过。gtest提供了一些常用的predicate:
testing::ExitedWithCode(exit_code)
如果程序正常退出并且退出码与exit_code相同则返回 true
testing::KilledBySignal(signal_number) // Windows下不支持
如果程序被signal_number信号kill的话就返回true
- 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风格表达式可参考:
-
POSIX extended regular expression
-
Wikipedia entry.
在Windows系统中,gtest的死亡测试中使用的是gtest自己实现的简单的正则表达式语法。 相比POSIX风格,gtest的简单正则表达式少了很多内容,比如 (“x|y”), (“(xy)”), (“[xy]”) 和(“x{5,7}”)都不支持。
下面是简单正则表达式支持的一些内容:
gtest定义两个宏,用来表示当前系统支持哪套正则表达式风格:
-
POSIX风格:GTEST_USES_POSIX_RE = 1
-
Simple风格:GTEST_USES_SIMPLE_RE=1
七、死亡测试运行方式
- fast方式(默认的方式)
testing::FLAGS_gtest_death_test_style = "fast";
- 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();
}
八、注意事项
-
不要在死亡测试里释放内存。
-
在父进程里再次释放内存。
-
不要在程序中使用内存堆检查。
九、总结
关于死亡测试,gtest官方的文档已经很详细了,同时在源码中也有大量的示例。如想了解更多的请参考官方的文档,或是直接看gtest源码。
简单来说,通过*_DEATH(statement, regex)和*_EXIT(statement, predicate, regex
),我们可以非常方便的编写导致崩溃的测试案例,并且在不影响其他案例执行的情况下,对崩溃案例的结果进行检查。
【系列链接】
【单元测试】C++单元测试框架Google Test入门之一 : 初识Gtest
【单元测试】C++单元测试框架Google Test入门之二 : 断言
【单元测试】C++单元测试框架Google Test入门之三 : 事件机制
【单元测试】C++单元测试框架Google Test入门之四 : 参数化
【单元测试】C++单元测试框架Google Test入门之五 : 死亡测试
【单元测试】C++单元测试框架Google Test入门之六 : 运行参数
【单元测试】C++单元测试框架Google Test入门之七 : 深入解析GTEST
【单元测试】C++单元测试框架Google Test入门之八 : 打造自己的单元测试框架