天天看點

玩轉Google開源C++單元測試架構Google Test系列(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`),我們可以非常友善的編寫導緻崩潰的測試案例,并且在不影響其他案例執行的情況下,對崩潰案例的結果進行檢查。

系列連結:

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)之八 - 打造自己的單元測試架構

轉載請注明本文位址: 玩轉Google開源C++單元測試架構Google Test系列(gtest)之五 - 死亡測試