Gtest:死亡測試


轉自:玩轉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`),我們可以非常方便的編寫導致崩潰的測試案例,並且在不影響其他案例執行的情況下,對崩潰案例的結果進行檢查。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM