如何用googletest寫單元測試
googletest是一個用來寫C++單元測試的框架,它是跨平臺的,可應用在windows、linux、Mac等OS平臺上。下面,我來說明如何使用最新的1.6版本gtest寫自己的單元測試。
本文包括以下幾部分:1、獲取並編譯googletest(以下簡稱為gtest);2、如何編寫單元測試用例;3、如何執行單元測試。4、google test內部是如何執行我們的單元測試用例的。
1. 獲取並編譯gtest
gtest試圖跨平臺,理論上,它就應該提供多個版本的binary包。但事實上,gtest只提供原始碼和相應平臺的編譯方式,這是為什麼呢?google的解釋是,我們在編譯出gtest時,有些獨特的工程很可能希望在編譯時加許多flag,把編譯的過程下放給使用者,可以讓使用者更靈活的處理。這個仁者見仁吧,反正也是免費的BSD許可權。
原始碼的獲取地址:http://code.google.com/p/googletest/downloads/list
目前gtest提供的是1.6.0版本,我們看看與以往版本1.5.0的區別:
|
就是最下面一行,make install禁用了,鬱悶了吧?UNIX的習慣編譯方法:./configure;make;make install失靈了,只能說google比較有種,又開始挑戰使用者習慣了。
那麼怎麼編譯呢?
先進入gtest目錄(解壓gtest.zip包過程就不說了),執行以下兩行命令:
- g++ -I./include -I./ -c ./src/gtest-all.cc
- ar -rv libgtest.a gtest-all.o
之後,生成了libgtest.a,這個就是我們要的東東了。以後寫自己的單元測試,就需要libgtest.a和gtest目錄下的include目錄,所以,這1檔案1目錄我們需要拷貝到自己的工程中。
編譯完成後怎麼驗證是否成功了呢?(相當不友好!)
- cd ${GTEST_DIR}/make
- make
- ./sample1_unittest
如果看到:
- Running main() from gtest_main.cc
- [==========] Running 6 tests from 2 test cases.
- [----------] Global test environment set-up.
- [----------] 3 tests from FactorialTest
- [ RUN ] FactorialTest.Negative
- [ OK ] FactorialTest.Negative (0 ms)
- [ RUN ] FactorialTest.Zero
- [ OK ] FactorialTest.Zero (0 ms)
- [ RUN ] FactorialTest.Positive
- [ OK ] FactorialTest.Positive (0 ms)
- [----------] 3 tests from FactorialTest (0 ms total)
- [----------] 3 tests from IsPrimeTest
- [ RUN ] IsPrimeTest.Negative
- [ OK ] IsPrimeTest.Negative (0 ms)
- [ RUN ] IsPrimeTest.Trivial
- [ OK ] IsPrimeTest.Trivial (0 ms)
- [ RUN ] IsPrimeTest.Positive
- [ OK ] IsPrimeTest.Positive (0 ms)
- [----------] 3 tests from IsPrimeTest (0 ms total)
- [----------] Global test environment tear-down
- [==========] 6 tests from 2 test cases ran. (0 ms total)
- [ PASSED ] 6 tests.
那麼證明編譯成功了。
2、如何編寫單元測試用例
以一個例子來說。我寫了一個開地址的雜湊表,它有del/get/add三個主要方法需要測試。在測試的時候,很自然,我只希望構造一個雜湊表物件,對之做許多種不同組合的操作,以驗證三個方法是否正常。所以,gtest提供的TEST方式我不會用,因為多個TEST不能共享同一份資料,而且還有初始化雜湊表物件的過程呢。所以我用TEST_F方式。TEST_F是一個巨集,TEST_F(classname, casename){}在函式體內去做具體的驗證。
上面是我要執行單元測試的類圖。那麼,我需要寫一系列單元測試用例來測試這個類。用gtest,首先要宣告一個類,繼承自gtest裡的Test類:
程式碼很簡單:
- class CHashTableTest : public ::testing::Test {
- protected:
- CHashTableTest():ht(100){
- }
- virtual void SetUp() {
- key1 = "testkey1";
- key2 = "testkey2";
- }
- // virtual void TearDown() {}
- CHashTable ht;
- string key1;
- string key2;
- };
然後開始寫測試用例,用例裡可以直接使用上面類中的成員。
- TEST_F(CHashTableTest, hashfunc)
- {
- CHashElement he;
- ASSERT_NE(\
- ht.getHashKey((char*)key1.c_str(), key1.size(), 0),\
- ht.getHashKey((char*)key2.c_str(), key2.size(), 0));
- ASSERT_NE(\
- ht.getHashKey((char*)key1.c_str(), key1.size(), 0),\
- ht.getHashKey((char*)key1.c_str(), key1.size(), 1));
- ASSERT_EQ(\
- ht.getHashKey((char*)key1.c_str(), key1.size(), 0),\
- ht.getHashKey((char*)key1.c_str(), key1.size(), 0));
- }
注意,TEST_F巨集會直接生成一個類,這個類繼承自上面我們寫的CHashTableTest類。
gtest提供ASSERT_和EXPECT_系列的巨集,用於判斷二進位制、字串等物件是否相等、真假等等。這兩種巨集的區別是,ASSERT_失敗了不會往下執行,而EXPECT_會繼續。
3、如何執行單元測試
首先,我們自己要有一個main函式,函式內容非常簡單:
- #include "gtest/gtest.h"
- int main(int argc, char** argv) {
- testing::InitGoogleTest(&argc, argv);
- // Runs all tests using Google Test.
- return RUN_ALL_TESTS();
- }
InitGoogleTest會解析引數。RUN_ALL_TESTS會把整個工程裡的TEST和TEST_F這些函式全部作為測試用例執行一遍。
執行時,假設我們編譯出的可執行檔案叫unittest,那麼直接執行./unittest就會輸出結果到螢幕,例如:
- [==========] Running 4 tests from 1 test case.
- [----------] Global test environment set-up.
- [----------] 4 tests from CHashTableTest
- [ RUN ] CHashTableTest.hashfunc
- [ OK ] CHashTableTest.hashfunc (0 ms)
- [ RUN ] CHashTableTest.addget
- [ OK ] CHashTableTest.addget (0 ms)
- [ RUN ] CHashTableTest.add2get
- testCHashTable.cpp:79: Failure
- Value of: getHe->m_pNext==NULL
- Actual: true
- Expected: false
- [ FAILED ] CHashTableTest.add2get (1 ms)
- [ RUN ] CHashTableTest.delget
- [ OK ] CHashTableTest.delget (0 ms)
- [----------] 4 tests from CHashTableTest (1 ms total)
- [----------] Global test environment tear-down
- [==========] 4 tests from 1 test case ran. (1 ms total)
- [ PASSED ] 3 tests.
- [ FAILED ] 1 test, listed below:
- [ FAILED ] CHashTableTest.add2get
可以看到,對於錯誤的CASE,會標出所在檔案及其行數。
如果我們需要輸出到XML檔案,則執行./unittest --gtest_output=xml,那麼會在當前目錄下生成test_detail.xml 檔案,內容如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <testsuites tests="3" failures="0" disabled="0" errors="0" time="0.001" name="AllTests">
- <testsuite name="CHashTableTest" tests="3" failures="0" disabled="0" errors="0" time="0.001">
- <testcase name="hashfunc" status="run" time="0.001" classname="CHashTableTest" />
- <testcase name="addget" status="run" time="0" classname="CHashTableTest" />
- <testcase name="delget" status="run" time="0" classname="CHashTableTest" />
- </testsuite>
- </testsuites>
如此,一個簡單的單元測試寫完。因為太簡單,所以不需要使用google mock模擬一些依賴。後續我再寫結合google mock來寫一些複雜的gtest單元測試。
下面來簡單說下gtest的工作流程。
4、google test內部是如何執行我們的單元測試用例的
首先從main函式看起。
我們的main函式執行了RUN_ALL_TESTS巨集,這個巨集幹了些什麼事呢?
- #define RUN_ALL_TESTS()\
- (::testing::UnitTest::GetInstance()->Run())
- } // namespace testing
原來是呼叫了UnitTest靜態工廠例項的Run方法!在gtest裡,一切測試用例都是Test類的例項!所以,Run方法將會執行所有的Test例項來執行所有的單元測試,看看類圖:
為什麼說一切單元測試用例都是Test類的例項呢?
我們有兩種寫測試用例的方法,一種就是上面我說的TEST_F巨集,這要求我們要顯示的定義一個子類繼承自Test類。在TEST_F巨集裡,會再次定義一個新類,繼承自我們上面定義的子類(兩重繼承哈)。
第二種就是TEST巨集,這個巨集裡不要求使用者程式碼定義類,但在google test裡,TEST巨集還是定義了一個子類繼承自Test類。
所以,UnitTest的Run方法只需要執行所有Test例項即可。
每個單元測試用例就是一個Test類子類的例項。它同時與TestResult,TestCase,TestInfo關聯起來,用於提供結果。
當然,還有EventListen類來監控結果的輸出,控制測試的進度等。
以上並沒有深入細節,只是大致幫助大家理解,我們寫的幾個簡單的gtest巨集,和單元測試用例,到底是如何被執行的。接下來,我會通過gmock來深入的看看google單元測試的玩法。
相關文章
- 如何用 JavaScript 編寫你的第一個單元測試JavaScript
- 如何寫好單元測試
- 如何寫出好的單元測試
- 單元測試-一份如何寫好單元測試的參考
- 如何優雅的寫單元測試?
- 首次在WebAPI中寫單元測試WebAPI
- 測試 之Java單元測試、Android單元測試JavaAndroid
- 單元測試:單元測試中的mockMock
- 如何編寫優秀的測試程式碼|單元測試
- 年輕時,我不寫單元測試
- 我不寫單元測試,被批了
- APISIX外掛如何編寫單元測試API
- 換種思路寫Mock,讓單元測試更簡單Mock
- 使用go優雅地撰寫單元測試Go
- 如何寫好測試用例以及go單元測試工具testify簡單介紹Go
- 單元測試,只是測試嗎?
- 單元測試-【轉】論單元測試的重要性
- SpringBoot單元測試Spring Boot
- python 單元測試Python
- iOS 單元測試iOS
- Flutter 單元測試Flutter
- 單元測試 Convey
- 單元測試真
- golang單元測試Golang
- 單元測試工具
- 前端單元測試前端
- 十五、單元測試
- Go單元測試Go
- 聊聊單元測試
- 編寫你的第一個 Android 單元測試Android
- 檔案上傳的單元測試怎麼寫?
- 前端測試:Part II (單元測試)前端
- 如何用 JMeter 編寫效能測試指令碼?JMeter指令碼
- 為程式碼編寫穩定的單元測試 [Go]Go
- JavaScript單元測試框架JavaScript框架
- 單元測試 -- mocha + chaiAI
- React元件單元測試React元件
- Spring Boot 單元測試Spring Boot
- Vue單元測試探索Vue