前幾個月Google開源了它的測試框架,自稱其旗下的上千個項目都在使用它。今天我們就用它來嘗嘗鮮吧?:-)
安裝:
下載Google C++ Testing Framework,解壓...
VC2005:
直接打開msvc\gtest.vcproj或msvc\gtest.sln,直接編譯即可。
Linux/Unix下的GCC:
傳統(tǒng)過程:./configure make
Mingw:
BCC:
用Mingw和BCB6編譯需要修改一些代碼,過幾天我會上傳到www.cppprog.com網(wǎng)站上。
使用:
首先#include <gtest/gtest.h>,當然工程的頭文件路徑要設(shè)置正確
1.簡單測試TEST
假如我寫了個函數(shù),是計算階乘的:
1. int Factorial( int n )
2. {
3. if(n==2) return 100;
4. return n<=0? 1 : n*Factorial(n - 1);
5. }
6.
7. TEST(TestFactorial, ZeroInput)
8. {
9. EXPECT_EQ(1, Factorial(0));
10. }
11. TEST(TestFactorial, OtherInput)
12. {
13. EXPECT_EQ(1, Factorial(1));
14. EXPECT_EQ(2, Factorial(2));
15. EXPECT_EQ(6, Factorial(3));
16. EXPECT_EQ(40320, Factorial(8));
17. }
18. int main(int argc, TCHAR* argv[])
19. {
20. testing::InitGoogleTest(&argc,argv);
21. RUN_ALL_TESTS();
22. std::cin.get();
23. return 0;
24. }
25.
運行結(jié)果:

瞧:測試框架指出:TestFactorial.ZeroInput運行OK,運行OtherInput時出現(xiàn)三次結(jié)果和預(yù)期不符。
2.多個測試場景需要相同數(shù)據(jù)配置的情況,用TEST_F
1.
2. typedef std::basic_string<TCHAR> tstring;
3. struct FooTest : testing::Test {
4.
5. tstring strExe;
6.
7. FooTest() {}
8. virtual ~FooTest() {}
9.
10. virtual void SetUp() {
11.
12. strExe.resize(MAX_PATH);
13. GetModuleFileName(NULL, &strExe[0], MAX_PATH);
14. }
15. virtual void TearDown() { }
16. };
17. tstring getfilename(const tstring &full)
18. {
19. return full.substr(full.rfind(_T('\\')));
20. }
21. tstring getpath(const tstring &full)
22. {
23. return full.substr(0, full.rfind(_T('\\')));
24. }
25. TEST_F(FooTest, Test_GFN)
26. {
27. EXPECT_STREQ(_T("Project1.exe"), getfilename(strExe).c_str());
28. }
29. TEST_F(FooTest, Test_GP)
30. {
31. EXPECT_STREQ(_T("D:\\Code\\libs\\google\\gtest-1.2.1\\BCC_SPC\\bcc\\ex"), getpath(strExe).c_str());
32. }
33. int main(int argc, TCHAR* argv[])
34. {
35. testing::InitGoogleTest(&argc,argv);
36. RUN_ALL_TESTS();
37. std::cin.get();
38. return 0;
39. }
運行結(jié)果:

瞧,Google C++ 測試框架毫不客氣地指出偶的getfilename返回的字符串比預(yù)期的多了一個'\\'
快速入門:
Google提供了兩種斷言形式,一種以ASSERT_開頭,另一種以EXPECT_開頭,它們的區(qū)別是ASSERT_*一旦失敗立馬退出,而EXPECT_還能繼續(xù)下去。
斷言列表:
真假條件測試:
致命斷言 | 非致命斷言 | 驗證條件 |
ASSERT_TRUE(condition); | EXPECT_TRUE(condition); | condition為真 |
ASSERT_FALSE(condition); | EXPECT_FALSE(condition); | condition 為假 |
數(shù)據(jù)對比測試:
致命斷言 | 非致命斷言 | 驗證條件 |
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字符串有相同的內(nèi)容 |
ASSERT_STRNE(str1, str2); | EXPECT_STRNE(str1, str2); | 兩個C字符串有不同的內(nèi)容 |
ASSERT_STRCASEEQ(expected_str, actual_str); | EXPECT_STRCASEEQ(expected_str, actual_str); | 兩個C字符串有相同的內(nèi)容,忽略大小寫 |
ASSERT_STRCASENE(str1, str2); | EXPECT_STRCASENE(str1, str2); | 兩個C字符串有不同的內(nèi)容,忽略大小寫 |
TEST宏:
TEST宏的作用是創(chuàng)建一個簡單測試,它定義了一個測試函數(shù),在這個函數(shù)里可以使用任何C++代碼并使用上面提供的斷言來進行檢查。
TEST的第一個參數(shù)是測試用例名,第二個參數(shù)是測試用例中某項測試的名稱。一個測試用例可以包含任意數(shù)量的獨立測試。這兩個參數(shù)組成了一個測試的全稱。
就前面的例子來說:
我們要測試這個函數(shù):int Factorial(int n); // 返回n的階乘
我們的測試用例是:測試輸入0的情況,測試輸入其它數(shù)據(jù)的情況,于是就有了:
1. TEST(TestFactorial, ZeroInput)
2. {
3. EXPECT_EQ(1, Factorial(0));
4. }
5. TEST(TestFactorial, OtherInput)
6. {
7. EXPECT_EQ(1, Factorial(1));
8. EXPECT_EQ(2, Factorial(2));
9. EXPECT_EQ(6, Factorial(3));
10. EXPECT_EQ(40320, Factorial(8));
11. }
Google Test根據(jù)測試用例來分組收集測試結(jié)果,因此,邏輯相關(guān)的測試應(yīng)該在同一測試用例中;換句話說,它們的TEST()的第一個參數(shù)應(yīng)該是一樣的。在上面的例子中,我們有兩個測試,ZeroInput和OtherInput,它們都屬于同一個測試用例TestFactorial。
TEST_F宏:
TEST_F宏用于在多個測試中使用同樣的數(shù)據(jù)配置,所以它又叫:測試夾具(Test Fixtures)
如果我們的多個測試要使用相同的數(shù)據(jù)(如前例中,我們的Test_GFN和Test_GP都使用程序自身的完整文件名來測試),就可以采用一個測試夾具。
要創(chuàng)建測試固件,只需:
1. 創(chuàng)建一個類繼承自testing::Test。將其中的成員聲明為protected:或是public:,因為我們想要從子類中存取夾具成員。
2. 在該類中聲明測試中所要使用到的數(shù)據(jù)。
3. 如果需要,編寫一個默認構(gòu)造函數(shù)或者SetUp()函數(shù)來為每個測試準備對象。
4. 如果需要,編寫一個析構(gòu)函數(shù)或者TearDown()函數(shù)來釋放你在SetUp()函數(shù)中申請的資源。
5. 如果需要,定義你的測試所需要共享的子程序。
當我們要使用固件時,使用TEST_F()替換掉TEST(),它允許我們存取測試固件中的對象和子程序:
TEST_F(test_case_name, test_name) {
... test body ...
}
與TEST()一樣,第一個參數(shù)是測試用例的名稱,但對TEST_F()來說,這個名稱必須與測試夾具類的名稱一樣。
對于TEST_F()中定義的每個測試,Google Test將會:
1. 創(chuàng)建一個全新的測試夾具
2. 通過SetUp()初始化它,
3. 運行測試
4. 調(diào)用TearDown()來進行清理工作
5. 刪除測試夾具。
注意,同一測試用例中,不同的測試擁有不同的測試夾具。Google Test不會對多個測試重用一個測試夾具,測試對測試夾具的改動并不會影響到其他測試。
調(diào)用測試
TEST()和TEST_F()向Google Test隱式注冊它們的測試。因此,與很多其他的C++測試框架不同,你不需要為了運行你定義的測試而將它們?nèi)吭倭谐鰜硪淮巍?
在定義好測試后,你可以通過RUN_ALL_TESTS()來運行它們,如果所有測試成功,該函數(shù)返回0,否則會返回1.注意RUN_ALL_TESTS()會運行你鏈接到的所有測試——它們可以來自不同的測試用例,甚至是來自不同的文件。
當被調(diào)用時,RUN_ALL_TESTS()宏會:
1. 保存所有的Google Test標志。
2. 為一個測試創(chuàng)建測試夾具對象。
3. 調(diào)用SetUp()初始化它。
4. 在固件對象上運行測試。
5. 調(diào)用TearDown()清理夾具。
6. 刪除固件。
7. 恢復所有Google Test標志的狀態(tài)。
8. 重復上訴步驟,直到所有測試完成。
此外,如果第二步時,測試夾具的構(gòu)造函數(shù)產(chǎn)生一個致命錯誤,繼續(xù)執(zhí)行3至5部顯然沒有必要,所以它們會被跳過。與之相似,如果第3部產(chǎn)生致命錯誤,第4部也會被跳過。
重要:你不能忽略掉RUN_ALL_TESTS()的返回值,否則gcc會報一個編譯錯誤。這樣設(shè)計的理由是自動化測試服務(wù)會根據(jù)測試退出返回碼來決定一個測試是否通過,而不是根據(jù)其stdout/stderr輸出;因此你的main()函數(shù)必須返回RUN_ALL_TESTS()的值。
而且,你應(yīng)該只調(diào)用RUN_ALL_TESTS()一次。多次調(diào)用該函數(shù)會與Google Test的一些高階特性(如線程安全死亡測試thread-safe death tests)沖突,因而是不被支持的。
編寫main()函數(shù)
你可以從下面這個模板開始:
1. #include "this/package/foo.h"
2. #include <gtest/gtest.h>
3. namespace {
4.
5. class FooTest : public testing::Test {
6. protected:
7.
8.
9. FooTest() {
10.
11. }
12. virtual ~FooTest() {
13.
14. }
15.
16.
17. virtual void SetUp() {
18.
19.
20. }
21. virtual void TearDown() {
22.
23.
24. }
25.
26. };
27.
28. TEST_F(FooTest, MethodBarDoesAbc) {
29. const string input_filepath = "this/package/testdata/myinputfile.dat";
30. const string output_filepath = "this/package/testdata/myoutputfile.dat";
31. Foo f;
32. EXPECT_EQ(0, f.Bar(input_filepath, output_filepath));
33. }
34.
35. TEST_F(FooTest, DoesXyz) {
36.
37. }
38. }
39. int main(int argc, char **argv) {
40. testing::InitGoogleTest(&argc, argv);
41. return RUN_ALL_TESTS();
42. }
testing::InitGoogleTest() 函數(shù)負責解析命令行傳入的Google Test標志,并刪除所有它可以處理的標志。這使得用戶可以通過各種不同的標志控制一個測試程序的行為。關(guān)于這一點我們會在GTestAdvanced中講到。你必須在調(diào)用RUN_ALL_TESTS()之前調(diào)用該函數(shù),否則就無法正確地初始化標示。
在Windows上InitGoogleTest()可以支持寬字符串,所以它也可以被用在以UNICODE模式編譯的程序中。
Google test for mingw 下載: http://www.cppprog.com/2009/0101/26.html Google test for bcb 下載: http://www.cppprog.com/2009/0101/27.html
發(fā)表于 @ 2008年12月30日 19:46:00|評論(13 )|收藏
您的文章已經(jīng)被推薦到CSDN首頁專家專欄欄目,將被更多的CSDN網(wǎng)友閱讀與分享。感謝您對CSDN博客的支持。
wslgz 發(fā)表于2008年12月31日 15:37:49 IP:舉報
不知道能不能給個vc的例子,有些地方不是很明白
wslgz 發(fā)表于2008年12月31日 15:37:58 IP:舉報
不知道能不能給個vc的例子,有些地方不是很明白
wslgz 發(fā)表于2008年12月31日 15:38:31 IP:舉報
不知道能不能給個vc的例子,有些地方不是很明白
同求vc的例子,最好6.0,^_^
同求vc的例子,最好6.0,^_^
同求vc的例子,最好6.0,^_^
VC是最簡單的,從官網(wǎng)上下載以后里面就自帶了VC的工程文件(包含例子和庫文件),直接打開編譯就可以了。
注意庫文件默認是“多線程”而不是“多線程DLL”。
我上面的代碼就是在VC2005里修改的。
至于VC6.0,我沒試過,不過可以考慮去下載我修改的for BCB6.0的代碼,VC6.0應(yīng)該也能編譯通過。
olay105 發(fā)表于2009年1月1日 18:29:28 IP:
總共15個,你知道怎么解決呢? 文章鏈接:http://blog.csdn.net/Waiting4you/archive/2008/12/30/3652350.aspx 發(fā)表時間:2009年1月1日 18:29:28">舉報
博主,我是第一次用vs2005,但是在實驗?zāi)愕奈恼轮谐霈F(xiàn)了很多問題,解決了一些,最后還是有一個棘手的,能幫我解決一下嗎?可能是編譯時的鏈接問題:error LNK2019: unresolved external symbol "protected: __thiscall testing::Test::Test(void)" (??0Test@testing@@IAE@XZ) referenced in function "public: __thiscall TestFactorial_ZeroInput_Test::TestFactorial_ZeroInput_Test(void)" (??0TestFactorial_ZeroInput_Test@@QAE@XZ) project1.obj
總共15個,你知道怎么解決呢?
回olay105:
先打開msvc\gtest.sln,編譯,生成Debug\gtestd.lib和Release\gtest.lib,這是庫文件。
把這個gtest.lib或gtestd.lib加入到這個“出現(xiàn)很多問題”的工程里。注意,因為庫文件工程里默認是“運行時庫:多線程”,所以你的工程也要這么設(shè)置。
謝謝博主,我已經(jīng)解決問題了,很開心!
樓主:
我直接使用VC2005編譯的話也是有很多錯誤,
錯誤 13 未能刪除文件“c:\code\googletest\msvc\Release\vc80.idb”。
請確保該文件未被其他進程打開并且未被寫保護。 gtest_output_test_
不知道是什么原因呢?我的環(huán)境里面還安裝了vc2003
謝謝。
樓主:
我直接使用VC2005編譯的話也是有很多錯誤,
錯誤 13 未能刪除文件“c:\code\googletest\msvc\Release\vc80.idb”。
請確保該文件未被其他進程打開并且未被寫保護。 gtest_output_test_
不知道是什么原因呢?我的環(huán)境里面還安裝了vc2003
謝謝。