开源C++单元测试框架Google Test介绍
開源C++單元測試框架Google Test介紹
Google Test
Google test是針對c/c++的開源測試項目。采用的協議是BSD license,有很多著名的開源項目采用了它,包括Chromium(谷歌瀏覽器開發版)。
安裝配置
下載主頁:
http://code.google.com/p/googletest/
官方資料文檔:
http://code.google.com/p/googletest/wiki/GoogleTestPrimer
http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide
當前的最新版本是1.5。包含3種tar.bz2,tar.gz和zip格式。解壓后的目錄結構:
其中的msvc就是VS的工程目錄,可以直接打開進行編譯(vs2008則需要進行工程升級轉化),生成相應的lib靜態庫文件。在vs中需要在工程中設置3個地方,和ACE的設置一樣:
1.設置gtest的頭文件
注:如果測試代碼需要上庫,附加包含目錄建議設置為相對路徑。
2.設置gtest的lib文件
注:如果測試代碼需要上庫,附加依賴項建議不要帶絕對路徑。
3.設置運行時的多線程庫支持
如果是Release版本,Runtime Library設為/MT。當然,其實你也可以選擇動態鏈接(/MD),前提是你之前編譯的gtest也使用了同樣是/MD選項。
如果是在Linux下,就比較方便,和普通的開源軟件一樣,采用
1…/configure –prefix=/your install path (如果不帶參數默認為/usr/local下面)
2.make
3.make install
然后就可以在工程中進行使用(如果指定了安裝目錄,則需要-I和-L來指明,同時也在最后的link加上-lpthread –lgtest)
下面是一個簡易的寫法:
By the way: 我在192.168.100.119上采用的是默認安裝,所以直接加上-lgtest和-lpthread就可以了
簡單例子
如果需要使用gtest,則需要包含
#include “gtest/gtest.h”
下面是一個簡單例子:
編譯運行的結果:
下面來依次解釋:
myadd是待測試函數名,TEST是作為gTest的一次測試(其實它是由gTest包裝過的一個宏),第一個參數Test_myadd是測試用例名,第二個參數IsReturnAdd是測試名(這兩個參數都是自己任意定義的)。在隨后的測試結果中將以“測試用例名.測試名”的形式呈現
EXPECT_EQ用于測試兩個數據是否相等。第一個參數可以是你提前預定義的數據,第二個為測試函數名。
main主函數中:
testing::InitGoogleTest用來處理程序的命令行參數。RUN_ALL_TEST也是一個宏,用來運行所有的測試用例(本例中就只有一個TEST)。測試結果英文也很清晰,我就不畫蛇添足了。
最后再補充一點,編譯后的二進制文件支持gtest的命令行參數,可以將數據直接轉化為xml
斷言
gtest采用了大量的宏來包裝斷言,此斷言不同于c語言的斷言(assert),按照使用方法分為2類:
1.ASSERT系列(ASSERT_*系列的斷言,當檢查點失敗時,退出當前函數,并非退出當前案例);
2.EXPECT系列(EXPECT_*系列的斷言,當檢查點失敗時,繼續往下運行)
按照常用功能依次分為12類,平常主要用到的是以下幾類:
1.布爾值比較
2.數值型數據比較
3.字符串比較
4.浮點數比較
5.近似數比較
6.異常檢測
7.自定義格式函數與參數檢查
布爾值比較
ASSERT_TRUE(condition) EXPECT_TRUE(condition) condition == true
ASSERT_FALSE(condition) EXPECT_FALSE(condition) condition == false
數值型數據比較
ASSERT_EQ(expected, actual) EXPECT_EQ(expected, actual) expected == actual
ASSERT_NE(val1, val2) EXPECT_NE(val1, val2) val1 != val2
ASSERT_LT(val1, val2) EXPECT_LT(val1, val2) val1 < val2
ASSERT_LE(val1, val2) EXPECT_LE(val1, val2) val1 <= val2
ASSERT_GT(val1, val2) EXPECT_GT(val1, val2) val1 > val2
ASSERT_GE(val1, val2) EXPECT_GE(val1, val2) val2 >= val2
字符串比較
ASSERT_STREQ(str1, str2) EXPECT_STREQ(str1, str2) 兩個C字符串內容相同(同時支持char *和wchar_t *類型)
ASSERT_STRNE(str1, str2) EXPECT_STRNE(str1, str2) 兩個C字符串內容不同(同時支持char *和wchar_t *類型)
ASSERT_STRCASEEQ(str1,str2) EXPECT_STRCASEEQ(str1,str2) 兩個C字符串內容相同,忽略大小寫(只支持char *類型)
ASSERT_STRCASENE(str1,str2) EXPECT_STRCASENE(str1,str2) 兩個C字符串內容不同,忽略大小寫(只支持char *類型)
浮點數比較
ASSERT_FLOAT_EQ(val1,val2) EXPECT_FLOAT_EQ(val1,val2) the two float values are almost equal
ASSERT_DOUBLE_EQ(val1,val2) EXPECT_DOUBLE_EQ(val1,val2) the two double values are almost equal
近似數比較
ASSERT_NEAR(val1, val2, abs_error) EXPECT_NEAR(val1, val2, abs_error) 兩個數值val1和val2的絕對值差不超過abs_error
異常檢查
ASSERT_THROW(statement, exception_type) EXPECT_THROW(statement, exception_type) 拋出指定類型異常
ASSERT_THROW(statement) EXPECT_THROW(statement) 拋出任意類型異常
ASSERT_NO_THROW(statement) EXPECT_NO_THROW(statement) 不拋異常
函數值與參數檢查(目前最多只支持5個參數)
ASSERT_PRED1(pred1, val1) EXPECT_PRED1(pred1, val1) pred1(val1) returns true
ASSERT_PRED2(pred2, val1, val2) EXPECT_PRED2(pred2, val1, val2) pred2(val1, val2) returns true
Windows HRESULT
ASSERT_HRESULT_SUCCEEDED
(expression) EXPECT_HRESULT_SUCCEEDED
(expression) expression is a success HRESULT
ASSERT_HRESULT_FAILED
(expression) EXPECT_HRESULT_FAILED
(expression) expression is a failure HRESULT
自定義格式函數與參數檢查(目前最多支持5個參數)
ASSERT_PRED_FORMAT1(pred1, val1) EXPECT_PRED_FORMAT1(pred1, val1) pred1(val1) is successful
ASSERT_PRED_FORMAT1(pred1, val1, val2) EXPECT_PRED_FORMAT1(pred1, val1, val2) pred2(val1, val2) is successful
下面將用一個實例來演示:
我們編寫了一個Configure的class,提供了3個對外的接口方法:
1.get_size(void)
2.add_item(string str)
3.get_item(int index)
現在需要對其進行測試,那么就應該依次有這3個文件:
1.Configure.h
2.Configure.cpp
3.main.cpp
首先是Configure.h:
接著是Configure.cpp:
最后是主函數調用:
進行編譯后的執行:
事件機制
Gtest提供了多種事件機制方便在測試用例之前或者完成以后進行一些操作,按照使用方法分為3類:
1.全局事件:在所有測試用例執行之前和完成之后生效。可以在全局事件中完成一些測試環境的初始化和資源回收工作,比如預留內存申請/回收,組件對象初始化/析構等。
2.TestSuite級別:在指定的測試套第一個測試用例之前,最后一個測試用例之后。如果根據子模塊定義測試套,那么就可以在TestSuite事件中完成一些子模塊的線程、消息隊列等的初始化和資源回收工作。
3.TestCase級別:在每個測試用例執行前后,即在每個測試代碼的斷言前后進行執行。
以下分別是3類事件的用法:
全局事件:必須通過繼承testing::Environment類,實現里面的SetUp和TearDown方法:
1.SetUp()方法在所有用例執行前執行;
2.TearDown()方法在所有用例執行后執行;
完成繼承類方法實現以后,還需要告訴gtest添加全局事件,我們需要在main函數中通過testing::AddGlobalTestEnvironment方法添加該全局事件。如果需要增加全局事件,也可以寫多個繼承類,然后將事件都添加到測試用例之前。
以下是運行全局事件以后的顯示結果:
TestSuite事件:需要通過繼承testing::Test類,實現里面的SetUpTestCase和TearDownTestCase兩個靜態方法:
1.SetUpTestCase()方法在TestSuit的第一個TestCase之前執行;
2.TearDownTestCase()方法在TestSuite的最后一個TestCase之后執行;
在編寫測試用例時,需要使用TEST_F宏,第一個參數必須是上面的繼承類名字,代表一個TestSuite。
以下是運行TestSuite事件以后的顯示結果:
TestCase事件:與TestSuite事件實現方法相同,需要通過繼承testing::Test類,但是只需要實現里面的SetUp和TearDown兩個方法:
1.SetUp()方法在每個TestCase之前執行;
2.TearDown()方法在每個TestCase之后執行;
在編寫測試用例時,需要使用TEST_F宏,第一個參數必須是上面的繼承類名字,代表一個TestSuite,并且在測試套中添加測試用例。
以下是運行TestSuite事件以后的顯示結果:
參數化
在設計測試案例時,經常需要考慮給被測函數傳入不同的值的情況,以前的做法一般是寫一個通用方法,然后編寫在測試案例調用它,即使使用了通用方法,也需要很多重復性的工作。以下是一般的測試方法,如果需要測試N個數字,則需要拷貝復制粘貼N次:
gTest在這里提供了一個靈活的函數參數化測試的方案:
1.告訴gtest參數類型:必須添加一個繼承類testing::TestWithParam,其中T就是需要參數化的參數類型。以上面為例,需要參數化一個int型的參數;
2.參數類型確定以后,需要使用一個新的宏TSET_P進行測試用例,在TEST_P宏里,使用GetParam()方法獲取當前的參數的具體值;
3.使用INSTANTIATE_TEST_CASE_P宏來確定測試的參數取值范圍;
其中:第一個參數是測試案例的前綴,可以任意取;第二個參數是測試案例的名稱,需要和之前定義的參數化的類的名稱相同,如:ParameterTest ;第三個參數是可以理解為參數生成器,上面的例子使用testing::Values表示使用括號內的參數。
Google提供了以下一系列的參數生成的函數:
Range(begin, end[, step]) 范圍在begin和end之間,步長為step,不包括end邊界
Values(v1, v2, …, vN) 從v1, v2到vN的值
ValuesIn(container),
ValuesIn(begin, end) 從一個C類型的數組或者STL容器或者迭代器中取值
Bool() 取false和true兩個值
Combine(g1, g2, …, gN) 將g1, g2, …, gN進行排列組合,g1, g2, …, gN本身是一個參數生成器,每次分別從g1, g2, …, gN中各取出一個值,組合成一個元組(Tuple)作為一個參數
說明:這個功能只在提供了<tr1/tuple>頭的系統中有效。gtest會自動去判斷是否支持tr/tuple,如果系統確實支持,而gtest判斷錯誤的話,可以重新定義宏GTEST_HAS_TR1_TUPLE = 1。
以下是執行函數參數化用例以后的顯示結果,最終輸出測試結果命名規則為:
ParameterTrueReturn/ParameterTest.TestParamName2/ValuesIndex
除了測試用例可以參數化以外,gtest還提供了針對各種不同類型數據時的方案,以及參數化類型的方案。下面以gtest的例子為例進行介紹:
1.首先需要定義一個模版類,從testing::Test類繼承;
2.然后再定義需要測試的具體數據類型,比如下面定義了測試char, int和unsigned int類型;
3.以下是執行類型參數化用例以后的顯示結果;
我們在這里使用函數參數化或者類型參數化,基本上就可以滿足正常單元測試需要。
死亡測試
“死亡測試”名字比較恐怖,這里的“死亡”是指程序的崩潰。通常在測試過程中,我們需要考慮各種各樣的輸入,有的輸入可能直接導致程序崩潰,這時我們就需要檢查程序是否按照預期的方式掛掉,這也就是所謂的“死亡測試”。gtest的死亡測試能做到在一個安全的環境下執行崩潰的測試案例,同時又對崩潰結果進行驗證。
死亡測試宏定義
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)和*_EXIT(statement, predicate, regex),我們可以非常方便的編寫導致崩潰的測試案例,并且在不影響其他案例執行的情況下,對崩潰案例的結果進行檢查。
以下是*_DEATH用法介紹:
1.statement是被測試的代碼語句,這里可以使用表達式,也可以直接調用函數結果;
2.regex是一個正則表達式,用來匹配異常時在stderr中輸出的內容;
3.編寫死亡測試案例時,TEST的第一個參數,即testcase_name,請使用DeathTest后綴。原因是gtest會優先運行死亡測試案例,應該是為線程安全考慮。
以上代碼中,通過執行get_throw(NULL)拋出異常來結束程序,在運行過程中gtest執行用例出錯以后沒有直接拋出異常,而是捕獲了此異常信息,以下是運行結果:
其中stderr內容通過正則表達式輸出:
在POSIX系統(Linux, Cygwin, 和 Mac)中,gtest的死亡測試中使用的是POSIX風格的正則表達式(關于POSIX風格的正則表達式資料請參照相關文檔),宏定義GTEST_USES_POSIX_RE = 1;
在Windows系統中,gtest的死亡測試中使用的是gtest自己實現的簡單的正則表達式語法。 相比POSIX風格,gtest的簡單正則表達式少了很多內容,比如 (“x|y”), ("(xy)"), ("[xy]") 和(“x{5,7}”)都不支持,宏定義GTEST_USES_SIMPLE_RE=1。
\d 匹配數字
\D 匹配非數字
\f 匹配 \f
\n 匹配 \n
\r 匹配 \r
\s 匹配所有ASCII字符(包括whitespace,\n)
\S 匹配非空格字符
\t 匹配 \t
\v 匹配 \v
\w 匹配所有字母,下劃線和數字
\W 匹配所有\w無法匹配的字母
\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
死亡測試有以下兩種運行方式:
1.fast方式(默認的方式):
testing::FLAGS_gtest_death_test_style = “fast”;
2.threadsafe方式:
testing::FLAGS_gtest_death_test_style = “threadsafe”;
可以在 main() 里為所有的死亡測試設置測試形式,也可以為某次測試單獨設置。gtest會在每次測試之前保存這個標記并在測試完成后恢復,所以可以不需要去管理這部分工作。
以下是所有死亡測試的設置方式:
以下是測試套死亡測試的設置方式:
注意事項:
1.不要在死亡測試里釋放內存;
2.不要在父進程里再次釋放內存;
3.不要在程序中使用內存堆檢查。
運行參數
使用gtest編寫的測試案例通常本身就是一個可執行文件,因此運行起來非常方便。同時,gtest也為我們提供了一系列的運行參數(環境變量、命令行參數或代碼里指定),使得我們可以對案例的執行進行一些有效的控制。gtest提供了三種設置的途徑:
1.系統環境變量
2.命令行參數
3.代碼中指定FLAG
其優先級原則是,最后設置的那個會生效。通常情況下的優先級順序為:命令行參數 > 代碼中指定FLAG > 系統環境變量。由于在gtest工程main函數中,gtest通過testing::InitGoogleTest方法直接處理輸入參數,因此測試用例可以處理命令行參數。
這樣就擁有了接收和響應gtest工程命令行參數的能力。如果需要在代碼中指定FLAG,可以使用testing::GTEST_FLAG這個宏來設置。比如相對于命令行參數–gtest_output,可以使用testing::GTEST_FLAG(output) = “xml:”;來設置。注意這里不需要加–gtest前綴了,同時,推薦將這句放置InitGoogleTest之前,這樣就可以使得對于同樣的參數,命令行參數優先級高于代碼中指定。
如果需要gtest的設置系統環境變量,必須注意的是:
1.系統環境變量全大寫,比如對于–gtest_output,相應的系統環境變量為GTEST_OUTPUT
2.有一個命令行參數例外,那就是–gtest_list_tests,它是不接受系統環境變量的,只是用來羅列測試案例名稱。
以下是所有命令行參數列表:
1.測試案例集合:
命令行參數 說明
–gtest_list_tests 使用這個參數時,將不會執行里面的測試案例,而是輸出一個案例的列表。
–gtest_filter 對執行的測試案例進行過濾,支持通配符
? 單個字符
- 任意字符
- 排除,如-a 表示除了a
: 取或,如a:b 表示a或b
比如下面的例子:
./foo_test 沒有指定過濾條件,運行所有案例;
./foo_test --gtest_filter=* 使用通配符*,表示運行所有案例;
./foo_test --gtest_filter=FooTest.* 運行所有“測試案例名稱(testcase_name)”為FooTest的案例;
./foo_test --gtest_filter=Null:Constructor 運行所有“測試案例名稱(testcase_name)”或“測試名稱(test_name)”包含Null或Constructor的案例;
./foo_test --gtest_filter=-DeathTest. 運行所有非死亡測試案例;
./foo_test --gtest_filter=FooTest.*-FooTest.Bar 運行所有“測試案例名稱(testcase_name)”為FooTest的案例,但是除了FooTest.Bar這個案例
–gtest_also_run_disabled_tests 執行案例時,同時也執行被置為無效的測試案例。關于設置測試案例無效的方法為:在測試案例名稱或測試名稱中添加DISABLED前綴。
–gtest_repeat=[COUNT] 設置案例重復運行次數:
–gtest_repeat=1000:重復執行1000次,即使中途出現錯誤;
–gtest_repeat=-1: 無限次數執行
–gtest_repeat=1000 --gtest_break_on_failure:重復執行1000次,并且在第一個錯誤發生時立即停止,這個功能對調試非常有用。
–gtest_repeat=1000 --gtest_filter=FooBar:重復執行1000次測試案例名稱為FooBar的案例。
2.測試案例輸出
命令行參數 說明
–gtest_color=(yes|no|auto) 輸出命令行時是否使用一些五顏六色的顏色,默認是auto。
–gtest_print_time 輸出命令行時是否打印每個測試案例的執行時間,默認是不打印的。
–gtest_output=
xml[:DIRECTORY_PATH|:FILE_PATH] 將測試結果輸出到一個xml中。
–gtest_output=xml: 不指定輸出路徑時,默認為案例當前路徑。
–gtest_output=xml:d:\ 指定輸出到某個目錄
–gtest_output=xml:d:\foo.xml 指定輸出到d:\foo.xml
如果不是指定了特定的文件路徑,gtest每次輸出的報告不會覆蓋,而會以數字后綴的方式創建。
3.對案例的異常處理
命令行參數 說明
–gtest_break_on_failure 調試模式下,當案例失敗時停止,方便調試。
–gtest_throw_on_failure 當案例失敗時以C++異常的方式拋出。
–gtest_catch_exceptions 是否捕捉異常。gtest默認是不捕捉異常的,因此假如測試案例拋了一個異常,很可能會彈出一個對話框,這非常的不友好,同時也阻礙了測試案例的運行。如果想不彈這個框,可以通過設置這個參數來實現。如將–gtest_catch_exceptions設置為一個非零的數。
注意:該參數只在Windows下有效。
以下是命令行參數列表在使用過程中遇到的一些問題總結:
1.同時使用–gtest_filter和–gtest_output=xml:時,在xml測試報告中能否只包含過濾后的測試案例的信息。
2.有時在代碼中設置 testing::GTEST_FLAG(catch_exceptions) = 1和在命令行中使用–gtest_catch_exceptions結果稍有不同,在代碼中設置FLAG方式有時候捕捉不了某些異常,但是通過命令行參數的方式一般都不會有問題。最后處理辦法是既在代碼中設置FLAG,又在命令行參數中傳入–gtest_catch_exceptions,估計是gtest在catch_exceptions方面不夠穩定的原因導致。
附件
以下是一些素材來源和更詳細的gtest介紹資料,本文檔暫時只提供一些基礎的介紹和演示,如果還需要更詳細的了解,建議閱讀gtest源代碼或者上網查找更深入詳細的資料:
總結
以上是生活随笔為你收集整理的开源C++单元测试框架Google Test介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu更新软件包
- 下一篇: 玩转Google开源C++单元测试框架