文章目錄
- Google Test
- 基本概念
- 編寫測試
- 頭文件
- Assertion
- TEST
- Test Fixture
- Invoking the Tests
- 編寫main()函數
- 參考
Google Test
Google Test是用于編寫C++測試的框架,支持多種類型的測試,而不是只有單元測試(unit test)。
編寫測試的要求:
- 測試應該是獨立且結果可重復的
- 測試應該是有組織的(organized),并且能夠反映被測試代碼的結構
- 測試應該是輕量(portable)且可重用的
- 如果測試未通過,應盡可能返回更多信息
- 測試框架應該能夠幫助測試者更多關注被測試的代碼,而不是過多關注如何編寫測試
- 測試應該能夠快速運行
Test定義:使用特定的輸入,運行特定的程序,并驗證運行的結果。
基本概念
使用GoogleTest的第一步是編寫assertion
,assertion
用來驗證某個條件是否為真,其結果可以是success,nonfatal failure或者是fatal failure。
如果測試報了fatal failure,則會中斷。
Test使用assertion
來檢驗代碼是否如期運行,如果測試出現問題,則測試失敗。
Test suite(測試組)包含一個或多個測試,組織test suite應檔能夠反映被測試代碼的結構。如果測試組中有測試需要共享對象或者寫成,應當放進test fixture
類中。
一個測試程序(test program)可包含多個測試組。
整體來說是個層級結構,從assertion
開始寫,組成test,test組成suite,suite組成測試程序。
編寫測試
頭文件
需要聲明以下頭文件
#include <gtest/gtest.h>
#include <glog/logging.h>
Assertion
Assertion是一種宏。包括下面兩種:
-
ASSERT_*
:如果fail,則產生fatal failures,并中斷當前函數。 -
EXPECT_*
:如果fail,產生nonfatal failures,不中斷。
EXPECT_*
能夠在一個test中報多個failure,通常來說更適合;當然,如果某個錯誤發生后,程序沒必要再繼續運行下去,則用ASSERT_*
更好。
可以通過<<
在assertion
后面添加額外的信息。
例子:
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";
for(int i = 0; i < x.size(); ++i){EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}
所有可被流式輸出的內容都可以被放在assertion信息里。
除了EQ
之外,還有很多種類型的assertion,可參考https://google.github.io/googletest/reference/assertions.html。
可能比較常用的:THAT,TRUE,FALSE,EQ,NE,LT,LE,GT,GE
在test1.cpp和axby.cpp中,用到了EQ和LE。
TEST
創建test的方法:
- 使用
TEST()
宏來定義和命名test函數;該函數是通常的C++函數,且不返回值 - 在函數中寫assertion
- test的結果取決于assertion,如果一個test內任意assertion報fail,則test報fail
TEST()
的參數按general到specific排列,第一個參數是suite名,第二個參數是test名,名稱中不能有下劃線_
。
例子:
int f(int n); // 函數聲明
TEST(fTest, ZeroInput){EXPECT_EQ(f(0), 1);
} // TEST 1
TEST(fTest, PositiveInput){EXPECT_EQ(f(1), 1);EXPECT_EQ(f(8), 512);
} // TEST 2
上例中,fTest是suite名,ZeroInput和PositiveInput為Test名。
邏輯上相關的test應當放在同一個suite中,即TEST()
的第一個參數應該相同。
Test Fixture
如果兩個或更多的test會使用同一個數據,則可以用test fixture來重用對象。
- 從
testing::Test
派生一個類,該類以protected:
起始 - 在這個派生類中,聲明要使用的對象
- 編寫默認的constructor或者SetUp()函數來配置每個test中的對象。
- 編寫destructor或者TearDown()來釋放在SetUp()中alloc的內存。
- 定義test中要用的subroutine。
用TEST_F()
來代替TEST()
,并將派生類的類名稱作為第一個傳參名稱。派生類要在TEST_F()
之前定義。
例子。以一個隊列類Queue
的測試為例,首先給出該類的接口:
template <typename E>
class Queue{public:Queue();void Enqueue(const E& element);E* Dequeue();size_t size() const;...
};
定義fixture class,習慣上命名為*Test
,*
為要測試的類名稱
class QueueTest : public testing::Test{protected:void SetUp() override{// q0_ remains emptyq1_.Enqueue(1);q2_.Enqueue(2);q2_.Enqueue(3);}Queue<int> q0_;Queue<int> q1_;Queue<int> q2_;
};
編寫test
TEST_F(QueueTest, IsEmptyInitially){EXPECT_EQ(q0_.size(), 0);
}
TEST_F(QueueTest, DqueueWorks){int * n = q0_.Dequeue();EXPECT_EQ(n, nullptr);n = q1_.Dequeue();ASSERT_NE(n, nullptr);EXPECT_EQ(*n, 1);EXPECT_EQ(q1_.size(), 0);delete ;n = q2_.Dequeue();ASSERT_NE(n, nullptr);EXPECT_EQ(*n, 2);EXPECT_EQ(q1_.size(), 1);delete ;
}
Invoking the Tests
在定義好test后,可以通過調用RUN_ALL_TESTS()來運行所有的test,如果所有的test都通過,則返回0,否則返回1。
該函數會測試所有可以關聯的test,包括不同test suite甚至不同文件中的test
編寫main()函數
通常情況可以不用寫main(),用gtest_main()就行。
如果需要在test之前做一些其他框架內沒法處理的操作,則可以自己寫個main(),并將返回值設為RUN_ALL_TESTS()
.
例子:
int main(int argc, char **argv){testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS();
}
testing::InitGoogleTest()
用于傳遞控制臺參數,使得用戶可以控制測試程序的行為,這部分可參考:https://google.github.io/googletest/advanced.html。
參考
- 用戶手冊:https://google.github.io/googletest/
- 入門:https://google.github.io/googletest/primer.html
- GitHub:https://github.com/google/googletest/tree/main