一、前言
“死亡測試”名字比較恐怖,這裡的“死亡”指的的是程式的崩潰。通常在測試過程中,我們需要考慮各種各樣的輸入,有的輸入可能直接導致程式崩潰,這時我們就需要檢查程式是否按照預期的方式掛掉,這也就是所謂的“死亡測試”。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是一個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是一個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
}
六、關於Regex
在POSIX系統(Linux, Cygwin, 和 Mac)中,gtest的死亡測試中使用的是POSIX風格的Regex,想瞭解POSIX風格運算式可參考:
1. POSIX extended regular expression
2. Wikipedia entry.
在Windows系統中,gtest的死亡測試中使用的是gtest自己實現的簡單的Regex文法。 相比POSIX風格,gtest的簡單Regex少了很多內容,比如 ("x|y"), ("(xy)"), ("[xy]") 和("x{5,7}")都不支援。
下面是簡單Regex支援的一些內容:
|
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定義兩個宏,用來表示當前系統支援哪套Regex風格:
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)之八 - 打造自己的單元測試架構