首先#include <gtest/gtest.h>,當然工程的頭文件路徑要設置正確
1.簡單測試TEST
Cpp代碼
#include <gtest/gtest.h>
int Factorial( int n )
{
if(n==2) return 100; //故意出個錯,嘻嘻
return n<=0? 1 : n*Factorial(n - 1);
}
//用TEST做簡單測試
TEST(TestFactorial, ZeroInput) //第一個參數是測試用例名,第二個參數是測試名:隨後的測試結果將以"測試用例名.測試名"的形式給出
{
EXPECT_EQ(1, Factorial(0)); //EXPECT_EQ稍候再說,現在只要知道它是測試兩個數據是否相等的就行了。
}
TEST(TestFactorial, OtherInput)
{
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
int main(int argc, char* argv[])
{
testing::InitGoogleTest(&argc,argv); //用來處理Test相關的命令行開關,如果不關注也可不加
RUN_ALL_TESTS(); //看函數名就知道干啥了
std::cin.get(); //只是讓它暫停而已,不然一閃就沒了
return 0;
}
[cpp] view plaincopyprint?#include <gtest/gtest.h>
int Factorial( int n )
{
if(n==2) return 100; //故意出個錯,嘻嘻
return n<=0? 1 : n*Factorial(n - 1);
}
//用TEST做簡單測試
TEST(TestFactorial, ZeroInput) //第一個參數是測試用例名,第二個參數是測試名:隨後的測試結果將以"測試用例名.測試名"的形式給出
{
EXPECT_EQ(1, Factorial(0)); //EXPECT_EQ稍候再說,現在只要知道它是測試兩個數據是否相等的就行了。
}
TEST(TestFactorial, OtherInput)
{
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
int main(int argc, char* argv[])
{
testing::InitGoogleTest(&argc,argv); //用來處理Test相關的命令行開關,如果不關注也可不加
RUN_ALL_TESTS(); //看函數名就知道干啥了
std::cin.get(); //只是讓它暫停而已,不然一閃就沒了
return 0;
}
#include <gtest/gtest.h>
int Factorial( int n )
{
if(n==2) return 100; //故意出個錯,嘻嘻
return n<=0? 1 : n*Factorial(n - 1);
}
//用TEST做簡單測試
TEST(TestFactorial, ZeroInput) //第一個參數是測試用例名,第二個參數是測試名:隨後的測試結果將以"測試用例名.測試名"的形式給出
{
EXPECT_EQ(1, Factorial(0)); //EXPECT_EQ稍候再說,現在只要知道它是測試兩個數據是否相等的就行了。
}
TEST(TestFactorial, OtherInput)
{
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
int main(int argc, char* argv[])
{
testing::InitGoogleTest(&argc,argv); //用來處理Test相關的命令行開關,如果不關注也可不加
RUN_ALL_TESTS(); //看函數名就知道干啥了
std::cin.get(); //只是讓它暫停而已,不然一閃就沒了
return 0;
}
2.多個測試場景需要相同數據配置的情況,用TEST_F
Cpp代碼
//用TEST_F做同配置的系列測試
typedef std::basic_string<TCHAR> tstring;
struct FooTest : testing::Test {
//這裡定義要測試的東東
tstring strExe;
//可以利用構造、析構來初始化一些參數
FooTest() {}
virtual ~FooTest() {}
//如果構造、析構還不能滿足你,還有下面兩個虛擬函數
virtual void SetUp() {
// 在構造後調用
strExe.resize(MAX_PATH);
GetModuleFileName(NULL, &strExe[0], MAX_PATH);
}
virtual void TearDown() { } // 在析構前調用
};
tstring getfilename(const tstring &full) //偶寫的從完整路徑裡取出文件名的函數(路徑分隔符假定為'\\')
{
return full.substr(full.rfind(_T('\\')));
}
tstring getpath(const tstring &full) //偶寫的從完整路徑裡取出路徑名的函數(Windows路徑)
{
return full.substr(0, full.rfind(_T('\\')));
}
TEST_F(FooTest, Test_GFN) //測試getfilename函數
{
EXPECT_STREQ(_T("Projectexe"), getfilename(strExe).c_str());
}
TEST_F(FooTest, Test_GP) //測試getpath函數
{
EXPECT_STREQ(_T("D:\\Code\\libs\\google\\gtest-1\\BCC_SPC\\bcc\\ex"), getpath(strExe).c_str());
}
int main(int argc, TCHAR* argv[]) //主函數還是一樣地
{
testing::InitGoogleTest(&argc,argv);
RUN_ALL_TESTS();
std::cin.get();
return 0;
}
[cpp] view plaincopyprint?//用TEST_F做同配置的系列測試
typedef std::basic_string<TCHAR> tstring;
struct FooTest : testing::Test {
//這裡定義要測試的東東
tstring strExe;
//可以利用構造、析構來初始化一些參數
FooTest() {}
virtual ~FooTest() {}
//如果構造、析構還不能滿足你,還有下面兩個虛擬函數
virtual void SetUp() {
// 在構造後調用
strExe.resize(MAX_PATH);
GetModuleFileName(NULL, &strExe[0], MAX_PATH);
}
virtual void TearDown() { } // 在析構前調用
};
tstring getfilename(const tstring &full) //偶寫的從完整路徑裡取出文件名的函數(路徑分隔符假定為'\\')
{
return full.substr(full.rfind(_T('\\')));
}
tstring getpath(const tstring &full) //偶寫的從完整路徑裡取出路徑名的函數(Windows路徑)
{
return full.substr(0, full.rfind(_T('\\')));
}
TEST_F(FooTest, Test_GFN) //測試getfilename函數
{
EXPECT_STREQ(_T("Projectexe"), getfilename(strExe).c_str());
}
TEST_F(FooTest, Test_GP) //測試getpath函數
{
EXPECT_STREQ(_T("D:\\Code\\libs\\google\\gtest-1\\BCC_SPC\\bcc\\ex"), getpath(strExe).c_str());
}
int main(int argc, TCHAR* argv[]) //主函數還是一樣地
{
testing::InitGoogleTest(&argc,argv);
RUN_ALL_TESTS();
std::cin.get();
return 0;
}
//用TEST_F做同配置的系列測試
typedef std::basic_string<TCHAR> tstring;
struct FooTest : testing::Test {
//這裡定義要測試的東東
tstring strExe;
//可以利用構造、析構來初始化一些參數
FooTest() {}
virtual ~FooTest() {}
//如果構造、析構還不能滿足你,還有下面兩個虛擬函數
virtual void SetUp() {
// 在構造後調用
strExe.resize(MAX_PATH);
GetModuleFileName(NULL, &strExe[0], MAX_PATH);
}
virtual void TearDown() { } // 在析構前調用
};
tstring getfilename(const tstring &full) //偶寫的從完整路徑裡取出文件名的函數(路徑分隔符假定為'\\')
{
return full.substr(full.rfind(_T('\\')));
}
tstring getpath(const tstring &full) //偶寫的從完整路徑裡取出路徑名的函數(Windows路徑)
{
return full.substr(0, full.rfind(_T('\\')));
}
TEST_F(FooTest, Test_GFN) //測試getfilename函數
{
EXPECT_STREQ(_T("Projectexe"), getfilename(strExe).c_str());
}
TEST_F(FooTest, Test_GP) //測試getpath函數
{
EXPECT_STREQ(_T("D:\\Code\\libs\\google\\gtest-1\\BCC_SPC\\bcc\\ex"), getpath(strExe).c_str());
}
int main(int argc, TCHAR* argv[]) //主函數還是一樣地
{
testing::InitGoogleTest(&argc,argv);
RUN_ALL_TESTS();
std::cin.get();
return 0;
}
快速入門:
Google提供了兩種斷言形式,一種以ASSERT_開頭,另一種以EXPECT_開頭,它們的區別是ASSERT_*一旦失敗立馬退出,而EXPECT_還能繼續下去。
斷言列表:
真假條件測試:
致命斷言 非致命斷言 驗證條件
ASSERT_TRUE(condition ); EXPECT_TRUE(condition ); condition 為真
ASSERT_FALSE(condition ); EXPECT_FALSE(condition ); condition 為假
數據對比測試:
致命斷言 非致命斷言 驗證條件
ASSERT_EQ(期望值 , 實際值 ); EXPECT_EQ(期望值 , 實際值 ); 期望值 == 實際值
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 ); val1 >= val2
字符串(針對C形式的字符串,即char*或wchar_t*)對比測試:
致命斷言 非致命斷言 驗證條件
ASSERT_STREQ(expected_str , actual_str ); EXPECT_STREQ(expected_str , actual_str ); 兩個C字符串有相同的內容
ASSERT_STRNE(str1 , str2 ); EXPECT_STRNE(str1 , str2 ); 兩個C字符串有不同的內容
ASSERT_STRCASEEQ(expected_str , actual_str ); EXPECT_STRCASEEQ(expected_str , actual_str ); 兩個C字符串有相同的內容,忽略大小寫
ASSERT_STRCASENE(str1 , str2 ); EXPECT_STRCASENE(str1 , str2 ); 兩個C字符串有不同的內容,忽略大小寫
TEST宏:
TEST宏的作用是創建一個簡單測試,它定義了一個測試函數,在這個函數裡可以使用任何C++代碼並使用上面提供的斷言來進行檢查。
TEST的第一個 參數是測試用例名,第二個 參數是測試用例中某項測試的名稱。一個測試用例可以包含任意數量的獨立測試。這兩個參數組成了一個測試的全稱。
就前面的例子來說:
我們要測試這個函數:int Factorial(int n); // 返回n的階乘
我們的測試用例是:測試輸入0的情況,測試輸入其它數據的情況,於是就有了:
TEST(TestFactorial, ZeroInput) //第一個參數是測試用例名,第二個參數是測試名:隨後的測試結果將以"測試用例名.測試名"的形式給出
{
EXPECT_EQ(1, Factorial(0)); //EXPECT_EQ稍候再說,現在只要知道它是測試兩個數據是否相等的就行了。
}
TEST(TestFactorial, OtherInput)
{
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
Google Test根據測試用例來分組收集測試結果,因此,邏輯相關的測試應該在同一測試用例中;換句話說,它們的TEST()的第一個參數應該是一樣的。在上面的例子中,我們有兩個測試,ZeroInput和OtherInput,它們都屬於同一個測試用例TestFactorial。
TEST_F宏:
TEST_F宏用於在多個測試中使用同樣的數據配置,所以它又叫:測試夾具(Test Fixtures)
如果我們的多個測試要使用相同的數據(如前例中,我們的Test_GFN和Test_GP都使用程序自身的完整文件名來測試),就可以采用一個測試夾具。
要創建測試固件,只需:
創建一個類繼承自testing::Test。將其中的成員聲明為protected:或是public:,因為我們想要從子類中存取夾具成員。
在該類中聲明測試中所要使用到的數據。
如果需要,編寫一個默認構造函數或者SetUp()函數來為每個測試准備對象。
如果需要,編寫一個析構函數或者TearDown()函數來釋放你在SetUp()函數中申請的資源。
如果需要,定義你的測試所需要共享的子程序。
當我們要使用固件時,使用TEST_F()替換掉TEST(),它允許我們存取測試固件中的對象和子程序:
TEST_F(test_case_name, test_name) {
... test body ...
}
與TEST()一樣,第一個參數是測試用例的名稱,但對TEST_F()來說,這個名稱必須與測試夾具類的名稱一樣。
對於TEST_F()中定義的每個 測試,Google Test將會:
創建一個全新的測試夾具
通過SetUp()初始化它,
運行測試
調用TearDown()來進行清理工作
刪除測試夾具。
注意,同一測試用例中,不同的測試擁有不同的測試夾具。Google Test不會對多個測試重用一個測試夾具,測試對測試夾具的改動並不會影響到其他測試。
調用測試
TEST()和TEST_F()向Google Test隱式注冊它們的測試。因此,與很多其他的C++測試框架不同,你不需要為了運行你定義的測試而將它們全部再列出來一次。
在定義好測試後,你可以通過RUN_ALL_TESTS()來運行它們,如果所有測試成功,該函數返回0,否則會返回1.注意RUN_ALL_TESTS()會運行你鏈接到的所有測試——它們可以來自不同的測試用例,甚至是來自不同的文件。
當被調用時,RUN_ALL_TESTS()宏會:
保存所有的Google Test標志。
為一個測試創建測試夾具對象。
調用SetUp()初始化它。
在固件對象上運行測試。
調用TearDown()清理夾具。
刪除固件。
恢復所有Google Test標志的狀態。
重復上訴步驟,直到所有測試完成。
此外,如果第二步時,測試夾具的構造函數產生一個致命錯誤,繼續執行3至5部顯然沒有必要,所以它們會被跳過。與之相似,如果第3部產生致命錯誤,第4部也會被跳過。
重要:你不能忽略掉RUN_ALL_TESTS()的返回值,否則gcc會報一個編譯錯誤。這樣設計的理由是自動化測試服務會根據測試退出返回碼來決定一個測試是否通過,而不是根據其stdout/stderr輸出;因此你的main()函數必須返回RUN_ALL_TESTS()的值。
而且,你應該只調用RUN_ALL_TESTS()一次。多次調用該函數會與Google Test的一些高階特性(如線程安全死亡測試thread-safe death tests)沖突,因而是不被支持的。
編寫 main() 函數
你可以從下面這個模板開始:
#include "this/package/foo.h"
#include <gtest/gtest.h>
namespace {
// 測試Foo類的測試固件
class FooTest : public testing::Test {
protected :
// You can remove any or all of the following functions if its body
// is empty.
FooTest() {
// You can do set-up work for each test here.
}
virtual ~FooTest() {
// You can do clean-up work that doesn't throw exceptions here.
}
// If the constructor and destructor are not enough for setting up
// and cleaning up each test, you can define the following methods:
virtual void SetUp() {
// Code here will be called immediately after the constructor (right
// before each test).
}
virtual void TearDown() {
// Code here will be called immediately after each test (right
// before the destructor).
}
// Objects declared here can be used by all tests in the test case for Foo.
};
// Tests that the Foo::Bar() method does Abc.
TEST_F(FooTest, MethodBarDoesAbc) {
const string input_filepath = "this/package/testdata/myinputfile.dat" ;
const string output_filepath = "this/package/testdata/myoutputfile.dat" ;
Foo f;
EXPECT_EQ(0, f.Bar(input_filepath, output_filepath));
}
// Tests that Foo does Xyz.
TEST_F(FooTest, DoesXyz) {
// Exercises the Xyz feature of Foo.
}
} // namespace
int main( int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
testing::InitGoogleTest() 函數負責解析命令行傳入的Google Test標志,並刪除所有它可以處理的標志。這使得用戶可以通過各種不同的標志控制一個測試程序的行為。關於這一點我們會在GTestAdvanced中講到。你必須在調用RUN_ALL_TESTS()之前調用該函數,否則就無法正確地初始化標示。
在Windows上InitGoogleTest()可以支持寬字符串,所以它也可以被用在以UNICODE模式編譯的程序中。