日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

玩转Google开源C++单元测试框架

發(fā)布時(shí)間:2023/12/14 c/c++ 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 玩转Google开源C++单元测试框架 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

玩轉(zhuǎn)Google開(kāi)源C++單元測(cè)試框架Google Test系列(gtest)(總)
前段時(shí)間學(xué)習(xí)和了解了下Google的開(kāi)源C++單元測(cè)試框架Google Test,簡(jiǎn)稱(chēng)gtest,非常的不錯(cuò)。 我們?cè)瓉?lái)使用的是自己實(shí)現(xiàn)的一套單元測(cè)試框架,在使用過(guò)程中,發(fā)現(xiàn)越來(lái)越多使用不便之處,而這樣不便之處,gtest恰恰很好的解決了。
其實(shí)gtest本身的實(shí)現(xiàn)并不復(fù)雜,我們完全可以模仿gtest,不斷的完善我們的測(cè)試框架, 但最后我們還是決定使用gtest取代掉原來(lái)的自己的測(cè)試框架,原因是:
1.不斷完善我們的測(cè)試框架之后就會(huì)發(fā)覺(jué)相當(dāng)于把gtest重新做了一遍,雖然輪子造的很爽,但是不是必要的。
2.使用gtest可以免去維護(hù)測(cè)試框架的麻煩,讓我們有更多精力投入到案例設(shè)計(jì)上。
3.gtest提高了非常完善的功能,并且簡(jiǎn)單易用,極大的提高了編寫(xiě)測(cè)試案例的效率。
gtest的官方網(wǎng)站是:
http://code.google.com/p/googletest/
從官方的使用文檔里,你幾乎可以獲得你想要的所有東西
http://code.google.com/p/googletest/wiki/GoogleTestPrimer

http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide

如果還想對(duì)gtest內(nèi)部探個(gè)究竟,就把它的代碼下載下來(lái)研究吧,這就是開(kāi)源的好處,哈!
官方已經(jīng)有如此完備的文檔了,為什么我還要寫(xiě)呢?一方面是自己記記筆記,好記性不如爛筆頭,以后自己想查查一些用法也可以直接在這里查到,一方面是對(duì)于不想去看一大堆英文文檔的朋友,在我這里可以快速的找到gtest相關(guān)的內(nèi)容。
玩轉(zhuǎn)Google開(kāi)源C++單元測(cè)試框架Google Test系列(gtest)之一 - 初識(shí)gtest
一、前言
本篇將介紹一些gtest的基本使用,包括下載,安裝,編譯,建立我們第一個(gè)測(cè)試Demo工程,以及編寫(xiě)一個(gè)最簡(jiǎn)單的測(cè)試案例。
二、下載
如果不記得網(wǎng)址, 直接在google里搜gtest,第一個(gè)就是。目前gtest的最新版本為1.3.0,從下列地址可以下載到該最新版本:
http://googletest.googlecode.com/files/gtest-1.3.0.zip
http://googletest.googlecode.com/files/gtest-1.3.0.tar.gz
http://googletest.googlecode.com/files/gtest-1.3.0.tar.bz2
三、編譯
下載解壓后, 里面有個(gè)msvc目錄:

使用VS的同學(xué)可以直接打開(kāi)msvc里面的工程文件, 如果你在使用的是VS2005或是VS2008,打開(kāi)后會(huì)提示你升級(jí),升完級(jí)后,我們直接編譯里面的“gtest”工程,可以直接編過(guò)的。
這里要提醒一下的是,如果你升級(jí)為VS2008的工程,那么你的測(cè)試Demo最好也是VS2008工程,不然你會(huì)發(fā)現(xiàn)很郁悶,你的Demo怎么也編不過(guò),我也曾折騰了好久,當(dāng)時(shí)我升級(jí)為了VS2008工程,結(jié)果我使用VS2005工程建Demo,死活編不過(guò)。(這里有人誤解了,并不是說(shuō)只能在VS2008中編譯,在VS2005中同樣可以。如果要編譯VS2005版本,最好保證gtest和你的測(cè)試工程都使用VS2005工程。)
編譯之后,在msvc里面的Debug或是Release目錄里看到編譯出來(lái)的gtestd.lib或是gtest.lib文件。
四、第一個(gè)Demo
下面我們開(kāi)始建立我們的第一個(gè)Demo了,假如之前使用的VS2008編譯的gtest,那么,我們?cè)赩S2008中,新建一個(gè)Win32 Console Application。接著就是設(shè)置工程屬性,總結(jié)如下:
1.設(shè)置gtest頭文件路徑

2.設(shè)置gtest.lib路徑

3.Runtime Library設(shè)置

如果是Release版本,Runtime Library設(shè)為/MT。當(dāng)然,其實(shí)你也可以選擇動(dòng)態(tài)鏈接(/MD),前提是你之前編譯的gtest也使用了同樣是/MD選項(xiàng)。
工程設(shè)置后了后,我們來(lái)編寫(xiě)一個(gè)最簡(jiǎn)單測(cè)試案例試試,我們先來(lái)寫(xiě)一個(gè)被測(cè)試函數(shù):

```cpp int Foo(int a, int b) {if (a == 0 || b == 0){throw "don't do that";}int c = a % b;if (c == 0)return b;return Foo(b, c); } 沒(méi)錯(cuò),上面的函數(shù)是用來(lái)求最大公約數(shù)的。下面我們就來(lái)編寫(xiě)一個(gè)簡(jiǎn)單的測(cè)試案例。```cpp #include <gtest/gtest.h>TEST(FooTest, HandleNoneZeroInput) {EXPECT_EQ(2, Foo(4, 10));EXPECT_EQ(6, Foo(30, 18)); }

上面可以看到,編寫(xiě)一個(gè)測(cè)試案例是多么的簡(jiǎn)單。 我們使用了TEST這個(gè)宏,它有兩個(gè)參數(shù),官方的對(duì)這兩個(gè)參數(shù)的解釋為:[TestCaseName,TestName],而我對(duì)這兩個(gè)參數(shù)的定義是:[TestSuiteName,TestCaseName],在下一篇我們?cè)賮?lái)看為什么這樣定義。
對(duì)檢查點(diǎn)的檢查,我們上面使用到了EXPECT_EQ這個(gè)宏,這個(gè)宏用來(lái)比較兩個(gè)數(shù)字是否相等。Google還包裝了一系列EXPECT_* 和ASSERT_的宏,而EXPECT系列和ASSERT系列的區(qū)別是:
1. EXPECT_ 失敗時(shí),案例繼續(xù)往下執(zhí)行。
2. ASSERT_* 失敗時(shí),直接在當(dāng)前函數(shù)中返回,當(dāng)前函數(shù)中ASSERT_*后面的語(yǔ)句將不會(huì)執(zhí)行。
在下一篇,我們?cè)賮?lái)具體討論這些斷言宏。為了讓我們的案例運(yùn)行起來(lái),我們還需要在main函數(shù)中添加如下代碼:

int _tmain(int argc, _TCHAR* argv[]) {testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS(); }

“testing::InitGoogleTest(&argc, argv);” :gtest的測(cè)試案例允許接收一系列的命令行參數(shù),因此,我們將命令行參數(shù)傳遞給gtest,進(jìn)行一些初始化操作。gtest的命令行參數(shù)非常豐富,在后面我們也會(huì)詳細(xì)了解到。
“RUN_ALL_TESTS()” :運(yùn)行所有測(cè)試案例
OK,一切就緒了,我們直接運(yùn)行案例試試(一片綠色,非常爽):

五、總結(jié)
本篇內(nèi)容確實(shí)是非常的初級(jí),目的是讓從來(lái)沒(méi)有接觸過(guò)gtest的同學(xué)了解gtest最基本的使用。gtest還有很多更高級(jí)的使用方法,我們將會(huì)在后面討論。總結(jié)本篇的內(nèi)容的話(huà):
1. 使用VS編譯gtest.lib文件
2. 設(shè)置測(cè)試工程的屬性(頭文件,lib文件,/MT參數(shù)(和編譯gtest時(shí)使用一樣的參數(shù)就行了))
3. 使用TEST宏開(kāi)始一個(gè)測(cè)試案例,使用EXPECT_*,ASSER_*系列設(shè)置檢查點(diǎn)。
4. 在Main函數(shù)中初始化環(huán)境,再使用RUN_ALL_TEST()宏運(yùn)行測(cè)試案例。
優(yōu)點(diǎn):
1. 我們的測(cè)試案例本身就是一個(gè)exe工程,編譯之后可以直接運(yùn)行,非常的方便。
2. 編寫(xiě)測(cè)試案例變的非常簡(jiǎn)單(使用一些簡(jiǎn)單的宏如TEST),讓我們將更多精力花在案例的設(shè)計(jì)和編寫(xiě)上。
3. 提供了強(qiáng)大豐富的斷言的宏,用于對(duì)各種不同檢查點(diǎn)的檢查。
4. 提高了豐富的命令行參數(shù)對(duì)案例運(yùn)行進(jìn)行一系列的設(shè)置。
玩轉(zhuǎn)Google開(kāi)源C++單元測(cè)試框架Google Test系列(gtest)之二 - 斷言
一、前言
這篇文章主要總結(jié)gtest中的所有斷言相關(guān)的宏。 gtest中,斷言的宏可以理解為分為兩類(lèi),一類(lèi)是ASSERT系列,一類(lèi)是EXPECT系列。一個(gè)直觀的解釋就是:

  • ASSERT_* 系列的斷言,當(dāng)檢查點(diǎn)失敗時(shí),退出當(dāng)前函數(shù)(注意:并非退出當(dāng)前案例)。
  • EXPECT_* 系列的斷言,當(dāng)檢查點(diǎn)失敗時(shí),繼續(xù)往下執(zhí)行。
    二、示例
    // int型比較,預(yù)期值:3,實(shí)際值:Add(1, 2)
    EXPECT_EQ(3, Add(1, 2))
    //
  • 假如你的Add(1, 2) 結(jié)果為4的話(huà),會(huì)在結(jié)果中輸出:
    g:\myproject\c++\gtestdemo\gtestdemo\gtestdemo.cpp(16): error: Value of: Add(1, 2)
    Actual: 4
    Expected:3
    如果是將結(jié)果輸出到xml里的話(huà),將輸出:(關(guān)于將結(jié)果輸出為xml,見(jiàn):http://www.cnblogs.com/coderzh/archive/2009/04/10/1432789.html)

    <![CDATA[g:\myproject\c++\gtestdemo\gtestdemo\gtestdemo.cpp:16 Value of: Add(1, 2) Actual: 4 Expected: 3]]>

    如果你對(duì)自動(dòng)輸出的出錯(cuò)信息不滿(mǎn)意的話(huà),你還可以通過(guò)操作符<<將一些自定義的信息輸出,通常,這對(duì)于調(diào)試或是對(duì)一些檢查點(diǎn)的補(bǔ)充說(shuō)明來(lái)說(shuō),非常有用!
    下面舉個(gè)例子:

    如果不使用<<操作符自定義輸出的話(huà):

    for (int i = 0; i < x.size(); ++i) {EXPECT_EQ(x[i], y[i]); }

    看到的結(jié)果將是這樣的,你根本不知道出錯(cuò)時(shí) i 等于幾:
    g:\myproject\c++\gtestdemo\gtestdemo\gtestdemo.cpp(25): error: Value of: y[i]
    Actual: 4
    Expected: x[i]
    Which is: 3

    如果使用<<操作符將一些重要信息輸出的話(huà):

    for (int i = 0; i < x.size(); ++i) {EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i; }

    從輸出結(jié)果中就可以定位到在 i = 2 時(shí)出現(xiàn)了錯(cuò)誤。這樣的輸出結(jié)果看起來(lái)更加有用,容易理解:
    g:\myproject\c++\gtestdemo\gtestdemo\gtestdemo.cpp(25): error: Value of: y[i]
    Actual: 4
    Expected: x[i]
    Which is: 3
    Vectors x and y differ at index 2

    三、布爾值檢查
    Fatal assertion Nonfatal assertion Verifies
    ASSERT_TRUE(condition); EXPECT_TRUE(condition); condition is true
    ASSERT_FALSE(condition); EXPECT_FALSE(condition); condition is false

    四、數(shù)值型數(shù)據(jù)檢查
    Fatal assertion Nonfatal assertion Verifies
    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); val1 >= val2

    五、字符串檢查
    Fatal assertion Nonfatal assertion Verifies
    ASSERT_STREQ(expected_str, actual_str); EXPECT_STREQ(expected_str, actual_str); the two C strings have the same content
    ASSERT_STRNE(str1, str2); EXPECT_STRNE(str1, str2); the two C strings have different content
    ASSERT_STRCASEEQ(expected_str, actual_str); EXPECT_STRCASEEQ(expected_str, actual_str); the two C strings have the same content, ignoring case
    ASSERT_STRCASENE(str1, str2); EXPECT_STRCASENE(str1, str2); the two C strings have different content, ignoring case

    STREQ和STRNE同時(shí)支持char和wchar_t類(lèi)型的,STRCASEEQ和STRCASENE卻只接收char*,估計(jì)是不常用吧。下面是幾個(gè)例子:

    TEST(StringCmpTest, Demo) {char* pszCoderZh = "CoderZh";wchar_t* wszCoderZh = L"CoderZh";std::string strCoderZh = "CoderZh";std::wstring wstrCoderZh = L"CoderZh";EXPECT_STREQ("CoderZh", pszCoderZh);EXPECT_STREQ(L"CoderZh", wszCoderZh);EXPECT_STRNE("CnBlogs", pszCoderZh);EXPECT_STRNE(L"CnBlogs", wszCoderZh);EXPECT_STRCASEEQ("coderzh", pszCoderZh);//EXPECT_STRCASEEQ(L"coderzh", wszCoderZh); 不支持EXPECT_STREQ("CoderZh", strCoderZh.c_str());EXPECT_STREQ(L"CoderZh", wstrCoderZh.c_str()); }

    六、顯示返回成功或失敗
    直接返回成功:SUCCEED();
    返回失敗:
    Fatal assertion Nonfatal assertion
    FAIL(); ADD_FAILURE();

    TEST(ExplicitTest, Demo) {ADD_FAILURE() << "Sorry"; // None Fatal Asserton,繼續(xù)往下執(zhí)行。//FAIL(); // Fatal Assertion,不往下執(zhí)行該案例。SUCCEED(); }

    七、異常檢查
    Fatal assertion Nonfatal assertion Verifies
    ASSERT_THROW(statement, exception_type); EXPECT_THROW(statement, exception_type); statement throws an exception of the given type
    ASSERT_ANY_THROW(statement); EXPECT_ANY_THROW(statement); statement throws an exception of any type
    ASSERT_NO_THROW(statement); EXPECT_NO_THROW(statement); statement doesn’t throw any exception

    例如:

    int Foo(int a, int b) {if (a == 0 || b == 0){throw "don't do that";}int c = a % b;if (c == 0)return b;return Foo(b, c); }TEST(FooTest, HandleZeroInput) {EXPECT_ANY_THROW(Foo(10, 0));EXPECT_THROW(Foo(0, 5), char*); }

    八、Predicate Assertions
    在使用EXPECT_TRUE或ASSERT_TRUE時(shí),有時(shí)希望能夠輸出更加詳細(xì)的信息,比如檢查一個(gè)函數(shù)的返回值TRUE還是FALSE時(shí),希望能夠輸出傳入的參數(shù)是什么,以便失敗后好跟蹤。因此提供了如下的斷言:
    Fatal assertion Nonfatal assertion Verifies
    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
    … … …

    Google人說(shuō)了,他們只提供<=5個(gè)參數(shù)的,如果需要測(cè)試更多的參數(shù),直接告訴他們。下面看看這個(gè)東西怎么用。

    bool MutuallyPrime(int m, int n) {return Foo(m , n) > 1; }TEST(PredicateAssertionTest, Demo) {int m = 5, n = 6;EXPECT_PRED2(MutuallyPrime, m, n); }

    當(dāng)失敗時(shí),返回錯(cuò)誤信息:
    error: MutuallyPrime(m, n) evaluates to false, where
    m evaluates to 5
    n evaluates to 6
    如果對(duì)這樣的輸出不滿(mǎn)意的話(huà),還可以自定義輸出格式,通過(guò)如下:
    Fatal assertion Nonfatal assertion Verifies
    ASSERT_PRED_FORMAT1(pred_format1, val1);` EXPECT_PRED_FORMAT1(pred_format1, val1); pred_format1(val1) is successful
    ASSERT_PRED_FORMAT2(pred_format2, val1, val2); EXPECT_PRED_FORMAT2(pred_format2, val1, val2); pred_format2(val1, val2) is successful
    … …

    用法示例:

    testing::AssertionResult AssertFoo(const char* m_expr, const char* n_expr, const char* k_expr, int m, int n, int k) {if (Foo(m, n) == k)return testing::AssertionSuccess();testing::Message msg;msg << m_expr << " 和 " << n_expr << " 的最大公約數(shù)應(yīng)該是:" << Foo(m, n) << " 而不是:" << k_expr;return testing::AssertionFailure(msg); }TEST(AssertFooTest, HandleFail) {EXPECT_PRED_FORMAT3(AssertFoo, 3, 6, 2); }

    失敗時(shí),輸出信息:
    error: 3 和 6 的最大公約數(shù)應(yīng)該是:3 而不是:2
    是不是更溫馨呢,呵呵。
    九、浮點(diǎn)型檢查
    Fatal assertion Nonfatal assertion Verifies
    ASSERT_FLOAT_EQ(expected, actual); EXPECT_FLOAT_EQ(expected, actual); the two float values are almost equal
    ASSERT_DOUBLE_EQ(expected, actual); EXPECT_DOUBLE_EQ(expected, actual); the two double values are almost equal

    對(duì)相近的兩個(gè)數(shù)比較:
    Fatal assertion Nonfatal assertion Verifies
    ASSERT_NEAR(val1, val2, abs_error); EXPECT_NEAR(val1, val2, abs_error); the difference between val1 and val2 doesn’t exceed the given absolute error

    同時(shí),還可以使用:
    EXPECT_PRED_FORMAT2(testing::FloatLE, val1, val2);
    EXPECT_PRED_FORMAT2(testing::DoubleLE, val1, val2);

    十、Windows HRESULT assertions
    Fatal assertion Nonfatal assertion Verifies
    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

    例如:
    CComPtr shell;
    ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application"));
    CComVariant empty;
    ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty, empty));

    十一、類(lèi)型檢查
    類(lèi)型檢查失敗時(shí),直接導(dǎo)致代碼編不過(guò),難得用處就在這?看下面的例子:

    template <typename T> class FooType { public:void Bar() { testing::StaticAssertTypeEq<int, T>(); } };TEST(TypeAssertionTest, Demo) {FooType<bool> fooType;fooType.Bar(); }

    玩轉(zhuǎn)Google開(kāi)源C++單元測(cè)試框架Google Test系列(gtest)之三 - 事件機(jī)制
    一、前言
    gtest提供了多種事件機(jī)制,非常方便我們?cè)诎咐盎蛑笞鲆恍┎僮鳌?偨Y(jié)一下gtest的事件一共有3種:

  • 全局的,所有案例執(zhí)行前后。
  • TestSuite級(jí)別的,在某一批案例中第一個(gè)案例前,最后一個(gè)案例執(zhí)行后。
  • TestCase級(jí)別的,每個(gè)TestCase前后。
    二、全局事件
    要實(shí)現(xiàn)全局事件,必須寫(xiě)一個(gè)類(lèi),繼承testing::Environment類(lèi),實(shí)現(xiàn)里面的SetUp和TearDown方法。
  • SetUp()方法在所有案例執(zhí)行前執(zhí)行
  • TearDown()方法在所有案例執(zhí)行后執(zhí)行
  • class FooEnvironment : public testing::Environment { public:virtual void SetUp(){std::cout << "Foo FooEnvironment SetUP" << std::endl;}virtual void TearDown(){std::cout << "Foo FooEnvironment TearDown" << std::endl;} };

    當(dāng)然,這樣還不夠,我們還需要告訴gtest添加這個(gè)全局事件,我們需要在main函數(shù)中通過(guò)testing::AddGlobalTestEnvironment方法將事件掛進(jìn)來(lái),也就是說(shuō),我們可以寫(xiě)很多個(gè)這樣的類(lèi),然后將他們的事件都掛上去。
    int _tmain(int argc, _TCHAR* argv[])
    {
    testing::AddGlobalTestEnvironment(new FooEnvironment);
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
    }

    三、TestSuite事件
    我們需要寫(xiě)一個(gè)類(lèi),繼承testing::Test,然后實(shí)現(xiàn)兩個(gè)靜態(tài)方法

  • SetUpTestCase() 方法在第一個(gè)TestCase之前執(zhí)行

  • TearDownTestCase() 方法在最后一個(gè)TestCase之后執(zhí)行

  • class FooTest : public testing::Test {protected:static void SetUpTestCase() {shared_resource_ = new ;}static void TearDownTestCase() {delete shared_resource_;shared_resource_ = NULL;}// Some expensive resource shared by all tests.static T* shared_resource_; };

    在編寫(xiě)測(cè)試案例時(shí),我們需要使用TEST_F這個(gè)宏,第一個(gè)參數(shù)必須是我們上面類(lèi)的名字,代表一個(gè)TestSuite。

    TEST_F(FooTest, Test1){// you can refer to shared_resource here } TEST_F(FooTest, Test2){// you can refer to shared_resource here }

    四、TestCase事件
    TestCase事件是掛在每個(gè)案例執(zhí)行前后的,實(shí)現(xiàn)方式和上面的幾乎一樣,不過(guò)需要實(shí)現(xiàn)的是SetUp方法和TearDown方法:

  • SetUp()方法在每個(gè)TestCase之前執(zhí)行
  • TearDown()方法在每個(gè)TestCase之后執(zhí)行
  • class FooCalcTest:public testing::Test { protected:virtual void SetUp(){m_foo.Init();}virtual void TearDown(){m_foo.Finalize();}FooCalc m_foo; };TEST_F(FooCalcTest, HandleNoneZeroInput) {EXPECT_EQ(4, m_foo.Calc(12, 16)); }TEST_F(FooCalcTest, HandleNoneZeroInput_Error) {EXPECT_EQ(5, m_foo.Calc(12, 16)); }

    五、總結(jié)
    gtest提供的這三種事件機(jī)制還是非常的簡(jiǎn)單和靈活的。同時(shí),通過(guò)繼承Test類(lèi),使用TEST_F宏,我們可以在案例之間共享一些通用方法,共享資源。使得我們的案例更加的簡(jiǎn)潔,清晰。
    玩轉(zhuǎn)Google開(kāi)源C++單元測(cè)試框架Google Test系列(gtest)之四 - 參數(shù)化
    一、前言
    在設(shè)計(jì)測(cè)試案例時(shí),經(jīng)常需要考慮給被測(cè)函數(shù)傳入不同的值的情況。我們之前的做法通常是寫(xiě)一個(gè)通用方法,然后編寫(xiě)在測(cè)試案例調(diào)用它。即使使用了通用方法,這樣的工作也是有很多重復(fù)性的,程序員都懶,都希望能夠少寫(xiě)代碼,多復(fù)用代碼。Google的程序員也一樣,他們考慮到了這個(gè)問(wèn)題,并且提供了一個(gè)靈活的參數(shù)化測(cè)試的方案。
    二、舊的方案
    為了對(duì)比,我還是把舊的方案提一下。首先我先把被測(cè)函數(shù)IsPrime帖過(guò)來(lái)(在gtest的example1.cc中),這個(gè)函數(shù)是用來(lái)判斷傳入的數(shù)值是否為質(zhì)數(shù)的。

    // Returns true iff n is a prime number.

    bool IsPrime(int n) {// Trivial case 1: small numbersif (n <= 1) return false;// Trivial case 2: even numbersif (n % 2 == 0) return n == 2;// Now, we have that n is odd and n >= 3.// Try to divide n by every odd number i, starting from 3for (int i = 3; ; i += 2) {// We only have to try i up to the squre root of nif (i > n/i) break;// Now, we have i <= n/i < n.// If n is divisible by i, n is not prime.if (n % i == 0) return false;}// n has no integer factor in the range (1, n), and thus is prime.return true; }

    假如我要編寫(xiě)判斷結(jié)果為T(mén)rue的測(cè)試案例,我需要傳入一系列數(shù)值讓函數(shù)IsPrime去判斷是否為T(mén)rue(當(dāng)然,即使傳入再多值也無(wú)法確保函數(shù)正確,呵呵),因此我需要這樣編寫(xiě)如下的測(cè)試案例:

    TEST(IsPrimeTest, HandleTrueReturn) {EXPECT_TRUE(IsPrime(3));EXPECT_TRUE(IsPrime(5));EXPECT_TRUE(IsPrime(11));EXPECT_TRUE(IsPrime(23));EXPECT_TRUE(IsPrime(17)); }

    我們注意到,在這個(gè)測(cè)試案例中,我至少?gòu)?fù)制粘貼了4次,假如參數(shù)有50個(gè),100個(gè),怎么辦?同時(shí),上面的寫(xiě)法產(chǎn)生的是1個(gè)測(cè)試案例,里面有5個(gè)檢查點(diǎn),假如我要把5個(gè)檢查變成5個(gè)單獨(dú)的案例,將會(huì)更加累人。
    接下來(lái),就來(lái)看看gtest是如何為我們解決這些問(wèn)題的。
    三、使用參數(shù)化后的方案

  • 告訴gtest你的參數(shù)類(lèi)型是什么
    你必須添加一個(gè)類(lèi),繼承testing::TestWithParam,其中T就是你需要參數(shù)化的參數(shù)類(lèi)型,比如上面的例子,我需要參數(shù)化一個(gè)int型的參數(shù)
    class IsPrimeParamTest : public::testing::TestWithParam
    {
  • };

  • 告訴gtest你拿到參數(shù)的值后,具體做些什么樣的測(cè)試
    這里,我們要使用一個(gè)新的宏(嗯,挺興奮的):TEST_P,關(guān)于這個(gè)"P"的含義,Google給出的答案非常幽默,就是說(shuō)你可以理解為”parameterized" 或者 “pattern”。我更傾向于 ”parameterized"的解釋,呵呵。在TEST_P宏里,使用GetParam()獲取當(dāng)前的參數(shù)的具體值。
  • TEST_P(IsPrimeParamTest, HandleTrueReturn) {int n = GetParam();EXPECT_TRUE(IsPrime(n)); }

    嗯,非常的簡(jiǎn)潔!
    3. 告訴gtest你想要測(cè)試的參數(shù)范圍是什么
    使用INSTANTIATE_TEST_CASE_P這宏來(lái)告訴gtest你要測(cè)試的參數(shù)范圍:
    INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17));

    第一個(gè)參數(shù)是測(cè)試案例的前綴,可以任意取。
    第二個(gè)參數(shù)是測(cè)試案例的名稱(chēng),需要和之前定義的參數(shù)化的類(lèi)的名稱(chēng)相同,如:IsPrimeParamTest
    第三個(gè)參數(shù)是可以理解為參數(shù)生成器,上面的例子使用test::Values表示使用括號(hào)內(nèi)的參數(shù)。Google提供了一系列的參數(shù)生成的函數(shù):
    Range(begin, end[, step]) 范圍在begin~end之間,步長(zhǎng)為step,不包括end
    Values(v1, v2, …, vN) v1,v2到vN的值
    ValuesIn(container) andValuesIn(begin, end) 從一個(gè)C類(lèi)型的數(shù)組或是STL容器,或是迭代器中取值
    Bool() 取false 和 true 兩個(gè)值
    Combine(g1, g2, …, gN) 這個(gè)比較強(qiáng)悍,它將g1,g2,…gN進(jìn)行排列組合,g1,g2,…gN本身是一個(gè)參數(shù)生成器,每次分別從g1,g2,…gN中各取出一個(gè)值,組合成一個(gè)元組(Tuple)作為一個(gè)參數(shù)。
    說(shuō)明:這個(gè)功能只在提供了<tr1/tuple>頭的系統(tǒng)中有效。gtest會(huì)自動(dòng)去判斷是否支持tr/tuple,如果你的系統(tǒng)確實(shí)支持,而gtest判斷錯(cuò)誤的話(huà),你可以重新定義宏GTEST_HAS_TR1_TUPLE=1。

    四、參數(shù)化后的測(cè)試案例名
    因?yàn)槭褂昧藚?shù)化的方式執(zhí)行案例,我非常想知道運(yùn)行案例時(shí),每個(gè)案例名稱(chēng)是如何命名的。我執(zhí)行了上面的代碼,輸出如下:

    從上面的框框中的案例名稱(chēng)大概能夠看出案例的命名規(guī)則,對(duì)于需要了解每個(gè)案例的名稱(chēng)的我來(lái)說(shuō),這非常重要。 命名規(guī)則大概為:
    prefix/test_case_name.test.name/index
    五、類(lèi)型參數(shù)化
    gtest還提供了應(yīng)付各種不同類(lèi)型的數(shù)據(jù)時(shí)的方案,以及參數(shù)化類(lèi)型的方案。我個(gè)人感覺(jué)這個(gè)方案有些復(fù)雜。首先要了解一下類(lèi)型化測(cè)試,就用gtest里的例子了。
    首先定義一個(gè)模版類(lèi),繼承testing::Test:

    template <typename T> class FooTest : public testing::Test {public:typedef std::list<T> List;static T shared_;T value_; };

    接著我們定義需要測(cè)試到的具體數(shù)據(jù)類(lèi)型,比如下面定義了需要測(cè)試char,int和unsigned int :
    typedef testing::Types<char, int, unsigned int> MyTypes;
    TYPED_TEST_CASE(FooTest, MyTypes);

    又是一個(gè)新的宏,來(lái)完成我們的測(cè)試案例,在聲明模版的數(shù)據(jù)類(lèi)型時(shí),使用TypeParam

    TYPED_TEST(FooTest, DoesBlah) {// Inside a test, refer to the special name TypeParam to get the type// parameter. Since we are inside a derived class template, C++ requires// us to visit the members of FooTest via 'this'.TypeParam n = this->value_;// To visit static members of the fixture, add the 'TestFixture::'// prefix.n += TestFixture::shared_;// To refer to typedefs in the fixture, add the 'typename TestFixture::'// prefix. The 'typename' is required to satisfy the compiler.typename TestFixture::List values;values.push_back(n);}

    上面的例子看上去也像是類(lèi)型的參數(shù)化,但是還不夠靈活,因?yàn)樾枰孪戎李?lèi)型的列表。gtest還提供一種更加靈活的類(lèi)型參數(shù)化的方式,允許你在完成測(cè)試的邏輯代碼之后再去考慮需要參數(shù)化的類(lèi)型列表,并且還可以重復(fù)的使用這個(gè)類(lèi)型列表。下面也是官方的例子:
    template
    class FooTest : public testing::Test {

    };

    TYPED_TEST_CASE_P(FooTest);

    接著又是一個(gè)新的宏TYPED_TEST_P類(lèi)完成我們的測(cè)試案例:

    TYPED_TEST_P(FooTest, DoesBlah) {
    // Inside a test, refer to TypeParam to get the type parameter.
    TypeParam n = 0;

    }

    TYPED_TEST_P(FooTest, HasPropertyA) { }

    接著,我們需要我們上面的案例,使用REGISTER_TYPED_TEST_CASE_P宏,第一個(gè)參數(shù)是testcase的名稱(chēng),后面的參數(shù)是test的名稱(chēng)
    REGISTER_TYPED_TEST_CASE_P(FooTest, DoesBlah, HasPropertyA);
    接著指定需要的類(lèi)型列表:
    typedef testing::Types<char, int, unsigned int> MyTypes;
    INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
    這種方案相比之前的方案提供更加好的靈活度,當(dāng)然,框架越靈活,復(fù)雜度也會(huì)隨之增加。
    六、總結(jié)
    gtest為我們提供的參數(shù)化測(cè)試的功能給我們的測(cè)試帶來(lái)了極大的方便,使得我們可以寫(xiě)更少更優(yōu)美的代碼,完成多種參數(shù)類(lèi)型的測(cè)試案例。
    玩轉(zhuǎn)Google開(kāi)源C++單元測(cè)試框架Google Test系列(gtest)之五 - 死亡測(cè)試
    一、前言
    “死亡測(cè)試”名字比較恐怖,這里的“死亡”指的的是程序的崩潰。通常在測(cè)試過(guò)程中,我們需要考慮各種各樣的輸入,有的輸入可能直接導(dǎo)致程序崩潰,這時(shí)我們就需要檢查程序是否按照預(yù)期的方式掛掉,這也就是所謂的“死亡測(cè)試”。gtest的死亡測(cè)試能做到在一個(gè)安全的環(huán)境下執(zhí)行崩潰的測(cè)試案例,同時(shí)又對(duì)崩潰結(jié)果進(jìn)行驗(yàn)證。
    二、使用的宏
    Fatal assertion Nonfatal assertion Verifies
    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,用來(lái)處理Debug和Realease下的不同。
    三、*_DEATH(statement, regex`)

  • statement是被測(cè)試的代碼語(yǔ)句
  • regex是一個(gè)正則表達(dá)式,用來(lái)匹配異常時(shí)在stderr中輸出的內(nèi)容
    如下面的例子:
  • void Foo() {int *pInt = 0;*pInt = 42 ; }TEST(FooDeathTest, Demo) {EXPECT_DEATH(Foo(), ""); }

    重要:編寫(xiě)死亡測(cè)試案例時(shí),TEST的第一個(gè)參數(shù),即testcase_name,請(qǐng)使用DeathTest后綴。原因是gtest會(huì)優(yōu)先運(yùn)行死亡測(cè)試案例,應(yīng)該是為線(xiàn)程安全考慮。
    四、*_EXIT(statement, predicate, regex`)

  • statement是被測(cè)試的代碼語(yǔ)句
  • predicate 在這里必須是一個(gè)委托,接收int型參數(shù),并返回bool。只有當(dāng)返回值為true時(shí),死亡測(cè)試案例才算通過(guò)。gtest提供了一些常用的predicate:
    testing::ExitedWithCode(exit_code)
  • 如果程序正常退出并且退出碼與exit_code相同則返回 true
    testing::KilledBySignal(signal_number) // Windows下不支持

    如果程序被signal_number信號(hào)kill的話(huà)就返回true
    3. regex是一個(gè)正則表達(dá)式,用來(lái)匹配異常時(shí)在stderr中輸出的內(nèi)容
    這里, 要說(shuō)明的是,_DEATH其實(shí)是對(duì)_EXIT進(jìn)行的一次包裝,*_DEATH的predicate判斷進(jìn)程是否以非0退出碼退出或被一個(gè)信號(hào)殺死。
    例子:

    TEST(ExitDeathTest, Demo) {EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), ""); }

    五、*_DEBUG_DEATH
    先來(lái)看定義:

    #ifdef NDEBUG#define EXPECT_DEBUG_DEATH(statement, regex) \do { statement; } while (false)#define ASSERT_DEBUG_DEATH(statement, regex) \do { statement; } while (false)#else#define EXPECT_DEBUG_DEATH(statement, regex) \EXPECT_DEATH(statement, regex)#define ASSERT_DEBUG_DEATH(statement, regex) \ASSERT_DEATH(statement, regex)#endif // NDEBUG for EXPECT_DEBUG_DEATH

    可以看到,在Debug版和Release版本下, *_DEBUG_DEATH的定義不一樣。因?yàn)楹芏喈惓V粫?huì)在Debug版本下拋出,而在Realease版本下不會(huì)拋出,所以針對(duì)Debug和Release分別做了不同的處理。看gtest里自帶的例子就明白了:

    int DieInDebugElse12(int* sideeffect) {if (sideeffect) *sideeffect = 12; #ifndef NDEBUGGTEST_LOG_(FATAL, "debug death inside DieInDebugElse12()"); #endif // NDEBUGreturn 12; }TEST(TestCase, TestDieOr12WorksInDgbAndOpt) {int sideeffect = 0;// Only asserts in dbg.EXPECT_DEBUG_DEATH(DieInDebugElse12(&sideeffect), "death");#ifdef NDEBUG// opt-mode has sideeffect visible.EXPECT_EQ(12, sideeffect);#else// dbg-mode no visible sideeffect.EXPECT_EQ(0, sideeffect);#endif }

    六、關(guān)于正則表達(dá)式
    在POSIX系統(tǒng)(Linux, Cygwin, 和 Mac)中,gtest的死亡測(cè)試中使用的是POSIX風(fēng)格的正則表達(dá)式,想了解POSIX風(fēng)格表達(dá)式可參考:

  • POSIX extended regular expression
  • Wikipedia entry.
    在Windows系統(tǒng)中,gtest的死亡測(cè)試中使用的是gtest自己實(shí)現(xiàn)的簡(jiǎn)單的正則表達(dá)式語(yǔ)法。 相比POSIX風(fēng)格,gtest的簡(jiǎn)單正則表達(dá)式少了很多內(nèi)容,比如 (“x|y”), ("(xy)"), ("[xy]") 和(“x{5,7}”)都不支持。
    下面是簡(jiǎn)單正則表達(dá)式支持的一些內(nèi)容:
    matches any literal character c
    \d matches any decimal digit
    \D matches any character that’s not a decimal digit
    \f matches \f
    \n matches \n
    \r matches \r
    \s matches any ASCII whitespace, including \n
    \S matches any character that’s not a whitespace
    \t matches \t
    \v matches \v
    \w matches any letter, _, or decimal digit
    \W matches any character that \w doesn’t match
    \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
  • gtest定義兩個(gè)宏,用來(lái)表示當(dāng)前系統(tǒng)支持哪套正則表達(dá)式風(fēng)格:

  • POSIX風(fēng)格:GTEST_USES_POSIX_RE = 1
  • Simple風(fēng)格:GTEST_USES_SIMPLE_RE=1
    七、死亡測(cè)試運(yùn)行方式
  • fast方式(默認(rèn)的方式)
    testing::FLAGS_gtest_death_test_style = “fast”;
  • threadsafe方式
    testing::FLAGS_gtest_death_test_style = “threadsafe”;
  • 你可以在 main() 里為所有的死亡測(cè)試設(shè)置測(cè)試形式,也可以為某次測(cè)試單獨(dú)設(shè)置。Google Test會(huì)在每次測(cè)試之前保存這個(gè)標(biāo)記并在測(cè)試完成后恢復(fù),所以你不需要去管這部分工作 。如:

    TEST(MyDeathTest, TestOne) {testing::FLAGS_gtest_death_test_style = "threadsafe";// This test is run in the "threadsafe" style:ASSERT_DEATH(ThisShouldDie(), ""); }TEST(MyDeathTest, TestTwo) {// This test is run in the "fast" style:ASSERT_DEATH(ThisShouldDie(), ""); }int main(int argc, char** argv) {testing::InitGoogleTest(&argc, argv);testing::FLAGS_gtest_death_test_style = "fast";return RUN_ALL_TESTS(); }

    八、注意事項(xiàng)

  • 不要在死亡測(cè)試?yán)镝尫艃?nèi)存。
  • 在父進(jìn)程里再次釋放內(nèi)存。
  • 不要在程序中使用內(nèi)存堆檢查。
    九、總結(jié)
    關(guān)于死亡測(cè)試,gtest官方的文檔已經(jīng)很詳細(xì)了,同時(shí)在源碼中也有大量的示例。如想了解更多的請(qǐng)參考官方的文檔,或是直接看gtest源碼。
    簡(jiǎn)單來(lái)說(shuō),通過(guò)*_DEATH(statement, regex)和*_EXIT(statement, predicate, regex),我們可以非常方便的編寫(xiě)導(dǎo)致崩潰的測(cè)試案例,并且在不影響其他案例執(zhí)行的情況下,對(duì)崩潰案例的結(jié)果進(jìn)行檢查。
    玩轉(zhuǎn)Google開(kāi)源C++單元測(cè)試框架Google Test系列(gtest)之六 - 運(yùn)行參數(shù)
    一、前言
    使用gtest編寫(xiě)的測(cè)試案例通常本身就是一個(gè)可執(zhí)行文件,因此運(yùn)行起來(lái)非常方便。同時(shí),gtest也為我們提供了一系列的運(yùn)行參數(shù)(環(huán)境變量、命令行參數(shù)或代碼里指定),使得我們可以對(duì)案例的執(zhí)行進(jìn)行一些有效的控制。
    二、基本介紹
    前面提到,對(duì)于運(yùn)行參數(shù),gtest提供了三種設(shè)置的途徑:
  • 系統(tǒng)環(huán)境變量
  • 命令行參數(shù)
  • 代碼中指定FLAG
    因?yàn)樘峁┝巳N途徑,就會(huì)有優(yōu)先級(jí)的問(wèn)題, 有一個(gè)原則是,最后設(shè)置的那個(gè)會(huì)生效。不過(guò)總結(jié)一下,通常情況下,比較理想的優(yōu)先級(jí)為:
    命令行參數(shù) > 代碼中指定FLAG > 系統(tǒng)環(huán)境變量
    為什么我們編寫(xiě)的測(cè)試案例能夠處理這些命令行參數(shù)呢?是因?yàn)槲覀冊(cè)趍ain函數(shù)中,將命令行參數(shù)交給了gtest,由gtest來(lái)搞定命令行參數(shù)的問(wèn)題。
    int _tmain(int argc, _TCHAR* argv[])
    {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
    }
  • 這樣,我們就擁有了接收和響應(yīng)gtest命令行參數(shù)的能力。如果需要在代碼中指定FLAG,可以使用testing::GTEST_FLAG這個(gè)宏來(lái)設(shè)置。比如相對(duì)于命令行參數(shù)–gtest_output,可以使用testing::GTEST_FLAG(output) = “xml:”;來(lái)設(shè)置。注意到了,不需要加–gtest前綴了。同時(shí),推薦將這句放置InitGoogleTest之前,這樣就可以使得對(duì)于同樣的參數(shù),命令行參數(shù)優(yōu)先級(jí)高于代碼中指定。

    int _tmain(int argc, _TCHAR* argv[]) {testing::GTEST_FLAG(output) = "xml:";testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS(); }

    最后再來(lái)說(shuō)下第一種設(shè)置方式-系統(tǒng)環(huán)境變量。如果需要gtest的設(shè)置系統(tǒng)環(huán)境變量,必須注意的是:

  • 系統(tǒng)環(huán)境變量全大寫(xiě),比如對(duì)于–gtest_output,響應(yīng)的系統(tǒng)環(huán)境變量為:GTEST_OUTPUT
  • 有一個(gè)命令行參數(shù)例外,那就是–gtest_list_tests,它是不接受系統(tǒng)環(huán)境變量的。(只是用來(lái)羅列測(cè)試案例名稱(chēng))
    三、參數(shù)列表
    了解了上面的內(nèi)容,我這里就直接將所有命令行參數(shù)總結(jié)和羅列一下。如果想要獲得詳細(xì)的命令行說(shuō)明,直接運(yùn)行你的案例,輸入命令行參數(shù):/? 或 --help 或 -help
  • 測(cè)試案例集合
    命令行參數(shù) 說(shuō)明
    –gtest_list_tests 使用這個(gè)參數(shù)時(shí),將不會(huì)執(zhí)行里面的測(cè)試案例,而是輸出一個(gè)案例的列表。
    –gtest_filter 對(duì)執(zhí)行的測(cè)試案例進(jìn)行過(guò)濾,支持通配符
    ? 單個(gè)字符
    • 任意字符
    • 排除,如,-a 表示除了a
      : 取或,如,a:b 表示a或b
      比如下面的例子:
      ./foo_test 沒(méi)有指定過(guò)濾條件,運(yùn)行所有案例
      ./foo_test --gtest_filter=* 使用通配符*,表示運(yùn)行所有案例
      ./foo_test --gtest_filter=FooTest.* 運(yùn)行所有“測(cè)試案例名稱(chēng)(testcase_name)”為FooTest的案例
      ./foo_test --gtest_filter=Null:Constructor 運(yùn)行所有“測(cè)試案例名稱(chēng)(testcase_name)”或“測(cè)試名稱(chēng)(test_name)”包含Null或Constructor的案例。
      ./foo_test --gtest_filter=-DeathTest. 運(yùn)行所有非死亡測(cè)試案例。
      ./foo_test --gtest_filter=FooTest.*-FooTest.Bar 運(yùn)行所有“測(cè)試案例名稱(chēng)(testcase_name)”為FooTest的案例,但是除了FooTest.Bar這個(gè)案例
      –gtest_also_run_disabled_tests 執(zhí)行案例時(shí),同時(shí)也執(zhí)行被置為無(wú)效的測(cè)試案例。關(guān)于設(shè)置測(cè)試案例無(wú)效的方法為:
      在測(cè)試案例名稱(chēng)或測(cè)試名稱(chēng)中添加DISABLED前綴,比如:

    // Tests that Foo does Abc.
    TEST(FooTest, DISABLED_DoesAbc) { }

    class DISABLED_BarTest : public testing::Test { };

    // Tests that Bar does Xyz.
    TEST_F(DISABLED_BarTest, DoesXyz) { }

    –gtest_repeat=[COUNT] 設(shè)置案例重復(fù)運(yùn)行次數(shù),非常棒的功能!比如:
    –gtest_repeat=1000 重復(fù)執(zhí)行1000次,即使中途出現(xiàn)錯(cuò)誤。
    –gtest_repeat=-1 無(wú)限次數(shù)執(zhí)行。。。。
    –gtest_repeat=1000 --gtest_break_on_failure 重復(fù)執(zhí)行1000次,并且在第一個(gè)錯(cuò)誤發(fā)生時(shí)立即停止。這個(gè)功能對(duì)調(diào)試非常有用。
    –gtest_repeat=1000 --gtest_filter=FooBar 重復(fù)執(zhí)行1000次測(cè)試案例名稱(chēng)為FooBar的案例。

  • 測(cè)試案例輸出
    命令行參數(shù) 說(shuō)明
    –gtest_color=(yes|no|auto) 輸出命令行時(shí)是否使用一些五顏六色的顏色。默認(rèn)是auto。
    –gtest_print_time 輸出命令行時(shí)是否打印每個(gè)測(cè)試案例的執(zhí)行時(shí)間。默認(rèn)是不打印的。
    –gtest_output=xml[:DIRECTORY_PATH|:FILE_PATH] 將測(cè)試結(jié)果輸出到一個(gè)xml中。
    1.–gtest_output=xml: 不指定輸出路徑時(shí),默認(rèn)為案例當(dāng)前路徑。
    2.–gtest_output=xml:d:\ 指定輸出到某個(gè)目錄
    3.–gtest_output=xml:d:\foo.xml 指定輸出到d:\foo.xml
    如果不是指定了特定的文件路徑,gtest每次輸出的報(bào)告不會(huì)覆蓋,而會(huì)以數(shù)字后綴的方式創(chuàng)建。xml的輸出內(nèi)容后面介紹吧。

  • 對(duì)案例的異常處理
    命令行參數(shù) 說(shuō)明
    –gtest_break_on_failure 調(diào)試模式下,當(dāng)案例失敗時(shí)停止,方便調(diào)試
    –gtest_throw_on_failure 當(dāng)案例失敗時(shí)以C++異常的方式拋出
    –gtest_catch_exceptions 是否捕捉異常。gtest默認(rèn)是不捕捉異常的,因此假如你的測(cè)試案例拋了一個(gè)異常,很可能會(huì)彈出一個(gè)對(duì)話(huà)框,這非常的不友好,同時(shí)也阻礙了測(cè)試案例的運(yùn)行。如果想不彈這個(gè)框,可以通過(guò)設(shè)置這個(gè)參數(shù)來(lái)實(shí)現(xiàn)。如將–gtest_catch_exceptions設(shè)置為一個(gè)非零的數(shù)。
    注意:這個(gè)參數(shù)只在Windows下有效。

  • 四、XML報(bào)告輸出格式

    <?xml version="1.0" encoding="UTF-8"?>

    從報(bào)告里可以看出,我們之前在TEST等宏中定義的測(cè)試案例名稱(chēng)(testcase_name)在xml測(cè)試報(bào)告中其實(shí)是一個(gè)testsuite name,而宏中的測(cè)試名稱(chēng)(test_name)在xml測(cè)試報(bào)告中是一個(gè)testcase name,概念上似乎有點(diǎn)混淆,就看你怎么看吧。
    當(dāng)檢查點(diǎn)通過(guò)時(shí),不會(huì)輸出任何檢查點(diǎn)的信息。當(dāng)檢查點(diǎn)失敗時(shí),會(huì)有詳細(xì)的失敗信息輸出來(lái)failure節(jié)點(diǎn)。
    在我使用過(guò)程中發(fā)現(xiàn)一個(gè)問(wèn)題,當(dāng)我同時(shí)設(shè)置了–gtest_filter參數(shù)時(shí),輸出的xml報(bào)告中還是會(huì)包含所有測(cè)試案例的信息,只不過(guò)那些不被執(zhí)行的測(cè)試案例的status值為“notrun”。而我之前認(rèn)為的輸出的xml報(bào)告應(yīng)該只包含我需要運(yùn)行的測(cè)試案例的信息。不知是否可提供一個(gè)只輸出需要執(zhí)行的測(cè)試案例的xml報(bào)告。因?yàn)楫?dāng)我需要在1000個(gè)案例中執(zhí)行其中1個(gè)案例時(shí),在報(bào)告中很難找到我運(yùn)行的那個(gè)案例,雖然可以查找,但還是很麻煩。
    五、總結(jié)
    本篇主要介紹了gtest案例執(zhí)行時(shí)提供的一些參數(shù)的使用方法,這些參數(shù)都非常有用。在實(shí)際編寫(xiě)gtest測(cè)試案例時(shí)肯定會(huì)需要用到的時(shí)候。至少我現(xiàn)在比較常用的就是:

  • –gtest_filter
  • –gtest_output=xml[:DIRECTORY_PATH|:FILE_PATH]
  • –gtest_catch_exceptions
    最后再總結(jié)一下我使用過(guò)程中遇到的幾個(gè)問(wèn)題:
  • 同時(shí)使用–gtest_filter和–gtest_output=xml:時(shí),在xml測(cè)試報(bào)告中能否只包含過(guò)濾后的測(cè)試案例的信息。
  • 有時(shí),我在代碼中設(shè)置 testing::GTEST_FLAG(catch_exceptions) = 1和我在命令行中使用–gtest_catch_exceptions結(jié)果稍有不同,在代碼中設(shè)置FLAG方式有時(shí)候捕捉不了某些異常,但是通過(guò)命令行參數(shù)的方式一般都不會(huì)有問(wèn)題。這是我曾經(jīng)遇到過(guò)的一個(gè)問(wèn)題,最后我的處理辦法是既在代碼中設(shè)置FLAG,又在命令行參數(shù)中傳入–gtest_catch_exceptions。不知道是gtest在catch_exceptions方面不夠穩(wěn)定,還是我自己測(cè)試案例的問(wèn)題。
    玩轉(zhuǎn)Google開(kāi)源C++單元測(cè)試框架Google Test系列(gtest)之七 - 深入解析gtest
    一、前言
    “深入解析”對(duì)我來(lái)說(shuō)的確有些難度,所以我盡量將我學(xué)習(xí)到和觀察到的gtest內(nèi)部實(shí)現(xiàn)介紹給大家。本文算是拋磚引玉吧,只能是對(duì)gtest的整體結(jié)構(gòu)的一些介紹,想要了解更多細(xì)節(jié)最好的辦法還是看gtest源碼,如果你看過(guò)gtest源碼,你會(huì)發(fā)現(xiàn)里面的注釋非常的詳細(xì)!好了,下面就開(kāi)始了解gtest吧。
    二、從TEST宏開(kāi)始
    前面的文章已經(jīng)介紹過(guò)TEST宏的用法了,通過(guò)TEST宏,我們可以非法簡(jiǎn)單、方便的編寫(xiě)測(cè)試案例,比如:
    TEST(FooTest, Demo)
    {
    EXPECT_EQ(1, 1);
    }
  • 我們先不去看TEST宏的定義,而是先使用/P參數(shù)將TEST展開(kāi)。如果使用的是Vistual Studio的話(huà):

  • 選中需要展開(kāi)的代碼文件,右鍵 - 屬性 - C/C++ - Preprocessor
  • Generate Preprocessed File 設(shè)置 Without Line Numbers (/EP /P) 或 With Line Numbers (/P)
  • 關(guān)閉屬性對(duì)話(huà)框,右鍵選中需要展開(kāi)的文件,右鍵菜單中點(diǎn)擊:Compile
    編譯過(guò)后,會(huì)在源代碼目錄生成一個(gè)后綴為.i的文件,比如我對(duì)上面的代碼進(jìn)行展開(kāi),展開(kāi)后的內(nèi)容為:
  • class FooTest_Demo_Test : public ::testing::Test { public: FooTest_Demo_Test() {} private: virtual void TestBody();static ::testing::TestInfo* const test_info_;FooTest_Demo_Test(const FooTest_Demo_Test &);void operator=(const FooTest_Demo_Test &); };::testing::TestInfo* const FooTest_Demo_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "FooTest", "Demo", "", "",(::testing::internal::GetTestTypeId()),::testing::Test::SetUpTestCase,::testing::Test::TearDownTestCase,new ::testing::internal::TestFactoryImpl< FooTest_Demo_Test>);void FooTest_Demo_Test::TestBody() {switch (0)case 0:if (const ::testing::AssertionResult gtest_ar = (::testing::internal:: EqHelper<(sizeof(::testing::internal::IsNullLiteralHelper(1)) == 1)>::Compare("1", "1", 1, 1)));else ::testing::internal::AssertHelper(::testing::TPRT_NONFATAL_FAILURE,".\\gtest_demo.cpp",9,gtest_ar.failure_message()) = ::testing::Message(); }

    展開(kāi)后,我們觀察到:

  • TEST宏展開(kāi)后,是一個(gè)繼承自testing::Test的類(lèi)。
  • 我們?cè)赥EST宏里面寫(xiě)的測(cè)試代碼,其實(shí)是被放到了類(lèi)的TestBody方法中。
  • 通過(guò)靜態(tài)變量test_info_,調(diào)用MakeAndRegisterTestInfo對(duì)測(cè)試案例進(jìn)行注冊(cè)。
    如下圖:
  • 上面關(guān)鍵的方法就是MakeAndRegisterTestInfo了,我們跳到MakeAndRegisterTestInfo函數(shù)中:

    // 創(chuàng)建一個(gè) TestInfo 對(duì)象并注冊(cè)到 Google Test;
    // 返回創(chuàng)建的TestInfo對(duì)象
    //
    // 參數(shù):
    //
    // test_case_name: 測(cè)試案例的名稱(chēng)
    // name: 測(cè)試的名稱(chēng)
    // test_case_comment: 測(cè)試案例的注釋信息
    // comment: 測(cè)試的注釋信息
    // fixture_class_id: test fixture類(lèi)的ID
    // set_up_tc: 事件函數(shù)SetUpTestCases的函數(shù)地址
    // tear_down_tc: 事件函數(shù)TearDownTestCases的函數(shù)地址
    // factory: 工廠(chǎng)對(duì)象,用于創(chuàng)建測(cè)試對(duì)象(Test)
    TestInfo* MakeAndRegisterTestInfo(
    const char* test_case_name, const char* name,
    const char* test_case_comment, const char* comment,
    TypeId fixture_class_id,
    SetUpTestCaseFunc set_up_tc,
    TearDownTestCaseFunc tear_down_tc,
    TestFactoryBase* factory) {
    TestInfo* const test_info =
    new TestInfo(test_case_name, name, test_case_comment, comment,
    fixture_class_id, factory);
    GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
    return test_info;
    }

    我們看到,上面創(chuàng)建了一個(gè)TestInfo對(duì)象,然后通過(guò)AddTestInfo注冊(cè)了這個(gè)對(duì)象。TestInfo對(duì)象到底是一個(gè)什么樣的東西呢?
    TestInfo對(duì)象主要用于包含如下信息:

  • 測(cè)試案例名稱(chēng)(testcase name)
  • 測(cè)試名稱(chēng)(test name)
  • 該案例是否需要執(zhí)行
  • 執(zhí)行案例時(shí),用于創(chuàng)建Test對(duì)象的函數(shù)指針
  • 測(cè)試結(jié)果
    我們還看到,TestInfo的構(gòu)造函數(shù)中,非常重要的一個(gè)參數(shù)就是工廠(chǎng)對(duì)象,它主要負(fù)責(zé)在運(yùn)行測(cè)試案例時(shí)創(chuàng)建出Test對(duì)象。我們看到我們上面的例子的factory為:
    new ::testing::internal::TestFactoryImpl< FooTest_Demo_Test>
  • 我們明白了,Test對(duì)象原來(lái)就是TEST宏展開(kāi)后的那個(gè)類(lèi)的對(duì)象(FooTest_Demo_Test),再看看TestFactoryImpl的實(shí)現(xiàn):
    template
    class TestFactoryImpl : public TestFactoryBase {
    public:
    virtual Test* CreateTest() { return new TestClass; }
    };

    這個(gè)對(duì)象工廠(chǎng)夠簡(jiǎn)單吧,嗯,Simple is better。當(dāng)我們需要?jiǎng)?chuàng)建一個(gè)測(cè)試對(duì)象(Test)時(shí),調(diào)用factory的CreateTest()方法就可以了。
    創(chuàng)建了TestInfo對(duì)象后,再通過(guò)下面的方法對(duì)TestInfo對(duì)象進(jìn)行注冊(cè):
    GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);

    GetUnitTestImpl()是獲取UnitTestImpl對(duì)象:
    inline UnitTestImpl* GetUnitTestImpl() {
    return UnitTest::GetInstance()->impl();
    }

    其中UnitTest是一個(gè)單件(Singleton),整個(gè)進(jìn)程空間只有一個(gè)實(shí)例,通過(guò)UnitTest::GetInstance()獲取單件的實(shí)例。上面的代碼看到,UnitTestImpl對(duì)象是最終是從UnitTest對(duì)象中獲取的。那么UnitTestImpl到底是一個(gè)什么樣的東西呢?可以這樣理解:
    UnitTestImpl是一個(gè)在UnitTest內(nèi)部使用的,為執(zhí)行單元測(cè)試案例而提供了一系列實(shí)現(xiàn)的那么一個(gè)類(lèi)。(自己歸納的,可能不準(zhǔn)確)
    我們上面的AddTestInfo就是其中的一個(gè)實(shí)現(xiàn),負(fù)責(zé)注冊(cè)TestInfo實(shí)例:

    // 添加TestInfo對(duì)象到整個(gè)單元測(cè)試中 // // 參數(shù): // // set_up_tc: 事件函數(shù)SetUpTestCases的函數(shù)地址 // tear_down_tc: 事件函數(shù)TearDownTestCases的函數(shù)地址 // test_info: TestInfo對(duì)象 void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,Test::TearDownTestCaseFunc tear_down_tc,TestInfo * test_info) { // 處理死亡測(cè)試的代碼,先不關(guān)注它 if (original_working_dir_.IsEmpty()) {original_working_dir_.Set(FilePath::GetCurrentDir());if (original_working_dir_.IsEmpty()) {printf("%s\n", "Failed to get the current working directory.");abort();} } // 獲取或創(chuàng)建了一個(gè)TestCase對(duì)象,并將testinfo添加到TestCase對(duì)象中。 GetTestCase(test_info->test_case_name(),test_info->test_case_comment(),set_up_tc,tear_down_tc)->AddTestInfo(test_info); }

    我們看到,TestCase對(duì)象出來(lái)了,并通過(guò)AddTestInfo添加了一個(gè)TestInfo對(duì)象。這時(shí),似乎豁然開(kāi)朗了:

  • TEST宏中的兩個(gè)參數(shù),第一個(gè)參數(shù)testcase_name,就是TestCase對(duì)象的名稱(chēng),第二個(gè)參數(shù)test_name就是Test對(duì)象的名稱(chēng)。而TestInfo包含了一個(gè)測(cè)試案例的一系列信息。
  • 一個(gè)TestCase對(duì)象對(duì)應(yīng)一個(gè)或多個(gè)TestInfo對(duì)象。
  • 我們來(lái)看看TestCase的創(chuàng)建過(guò)程(UnitTestImpl::GetTestCase):

    // 查找并返回一個(gè)指定名稱(chēng)的TestCase對(duì)象。如果對(duì)象不存在,則創(chuàng)建一個(gè)并返回
    //
    // 參數(shù):
    //
    // test_case_name: 測(cè)試案例名稱(chēng)
    // set_up_tc: 事件函數(shù)SetUpTestCases的函數(shù)地址
    // tear_down_tc: 事件函數(shù)TearDownTestCases的函數(shù)地址

    TestCase* UnitTestImpl::GetTestCase(const char* test_case_name,const char* comment,Test::SetUpTestCaseFunc set_up_tc,Test::TearDownTestCaseFunc tear_down_tc) {// 從test_cases里查找指定名稱(chēng)的TestCaseinternal::ListNode<TestCase*>* node = test_cases_.FindIf(TestCaseNameIs(test_case_name));if (node == NULL) {// 沒(méi)找到,我們來(lái)創(chuàng)建一個(gè)TestCase* const test_case =new TestCase(test_case_name, comment, set_up_tc, tear_down_tc);// 判斷是否為死亡測(cè)試案例if (internal::UnitTestOptions::MatchesFilter(String(test_case_name),kDeathTestCaseFilter)) {// 是的話(huà),將該案例插入到最后一個(gè)死亡測(cè)試案例后node = test_cases_.InsertAfter(last_death_test_case_, test_case);last_death_test_case_ = node;} else {// 否則,添加到test_cases最后。test_cases_.PushBack(test_case);node = test_cases_.Last();}}// 返回TestCase對(duì)象return node->element(); }

    三、回過(guò)頭看看TEST宏的定義
    #define TEST(test_case_name, test_name)
    GTEST_TEST_(test_case_name, test_name,
    ::testing::Test, ::testing::internal::GetTestTypeId())

    同時(shí)也看看TEST_F宏
    #define TEST_F(test_fixture, test_name)
    GTEST_TEST_(test_fixture, test_name, test_fixture,
    ::testing::internal::GetTypeId<test_fixture>())
    都是使用了GTEST_TEST_宏,在看看這個(gè)宏如何定義的:

    #define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)
    class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {
    public:
    GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}
    private:
    virtual void TestBody();
    static ::testing::TestInfo* const test_info_;
    GTEST_DISALLOW_COPY_AND_ASSIGN_(
    GTEST_TEST_CLASS_NAME_(test_case_name, test_name));
    };

    ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)
    ::test_info_ =
    ::testing::internal::MakeAndRegisterTestInfo(
    #test_case_name, #test_name, “”, “”,
    (parent_id),
    parent_class::SetUpTestCase,
    parent_class::TearDownTestCase,
    new ::testing::internal::TestFactoryImpl<
    GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);
    void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()

    不需要多解釋了,和我們上面展開(kāi)看到的差不多,不過(guò)這里比較明確的看到了,我們?cè)赥EST宏里寫(xiě)的就是TestBody里的東西。這里再補(bǔ)充說(shuō)明一下里面的GTEST_DISALLOW_COPY_AND_ASSIGN_宏,我們上面的例子看出,這個(gè)宏展開(kāi)后:
    FooTest_Demo_Test(const FooTest_Demo_Test &);
    void operator=(const FooTest_Demo_Test &);

    正如這個(gè)宏的名字一樣,它是用于防止對(duì)對(duì)象進(jìn)行拷貝和賦值操作的。
    四、再來(lái)了解RUN_ALL_TESTS宏
    我們的測(cè)試案例的運(yùn)行就是通過(guò)這個(gè)宏發(fā)起的。RUN_ALL_TEST的定義非常簡(jiǎn)單:
    #define RUN_ALL_TESTS()
    (::testing::UnitTest::GetInstance()->Run())

    我們又看到了熟悉的::testing::UnitTest::GetInstance(),看來(lái)案例的執(zhí)行時(shí)從UnitTest的Run方法開(kāi)始的,我提取了一些Run中的關(guān)鍵代碼,如下:

    int UnitTest::Run() {__try {return impl_->RunAllTests();} __except(internal::UnitTestOptions::GTestShouldProcessSEH(GetExceptionCode())) {printf("Exception thrown with code 0x%x.\nFAIL\n", GetExceptionCode());fflush(stdout);return 1;}return impl_->RunAllTests(); }

    我們又看到了熟悉的impl(UnitTestImpl),具體案例該怎么執(zhí)行,還是得靠UnitTestImpl。

    int UnitTestImpl::RunAllTests() {// ...printer->OnUnitTestStart(parent_);// 計(jì)時(shí)const TimeInMillis start = GetTimeInMillis();printer->OnGlobalSetUpStart(parent_);// 執(zhí)行全局的SetUp事件environments_.ForEach(SetUpEnvironment);printer->OnGlobalSetUpEnd(parent_);// 全局的SetUp事件執(zhí)行成功的話(huà)if (!Test::HasFatalFailure()) {// 執(zhí)行每個(gè)測(cè)試案例test_cases_.ForEach(TestCase::RunTestCase);}// 執(zhí)行全局的TearDown事件printer->OnGlobalTearDownStart(parent_);environments_in_reverse_order_.ForEach(TearDownEnvironment);printer->OnGlobalTearDownEnd(parent_);elapsed_time_ = GetTimeInMillis() - start;// 執(zhí)行完成printer->OnUnitTestEnd(parent_);// Gets the result and clears it.if (!Passed()) {failed = true;}ClearResult();// 返回測(cè)試結(jié)果return failed ? 1 : 0; }

    上面,我們很開(kāi)心的看到了我們前面講到的全局事件的調(diào)用。environments_是一個(gè)Environment的鏈表結(jié)構(gòu)(List),它的內(nèi)容是我們?cè)趍ain中通過(guò):
    testing::AddGlobalTestEnvironment(new FooEnvironment);

    添加進(jìn)去的。test_cases_我們之前也了解過(guò)了,是一個(gè)TestCase的鏈表結(jié)構(gòu)(List)。gtest實(shí)現(xiàn)了一個(gè)鏈表,并且提供了一個(gè)Foreach方法,迭代調(diào)用某個(gè)函數(shù),并將里面的元素作為函數(shù)的參數(shù):

    template <typename F> // F is the type of the function/functor void ForEach(F functor) const {for ( const ListNode<E> * node = Head();node != NULL;node = node->next() ) {functor(node->element());} }

    因此,我們關(guān)注一下:environments_.ForEach(SetUpEnvironment),其實(shí)是迭代調(diào)用了SetUpEnvironment函數(shù):
    static void SetUpEnvironment(Environment* env) { env->SetUp(); }

    最終調(diào)用了我們定義的SetUp()函數(shù)。
    再看看test_cases_.ForEach(TestCase::RunTestCase)的TestCase::RunTestCase實(shí)現(xiàn):
    static void RunTestCase(TestCase * test_case) { test_case->Run(); }

    再看TestCase的Run實(shí)現(xiàn):

    void TestCase::Run() {if (!should_run_) return;internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();impl->set_current_test_case(this);UnitTestEventListenerInterface * const result_printer =impl->result_printer();result_printer->OnTestCaseStart(this);impl->os_stack_trace_getter()->UponLeavingGTest();// 哈!SetUpTestCases事件在這里調(diào)用set_up_tc_();const internal::TimeInMillis start = internal::GetTimeInMillis();// 嗯,前面分析的一個(gè)TestCase對(duì)應(yīng)多個(gè)TestInfo,因此,在這里迭代對(duì)TestInfo調(diào)用RunTest方法test_info_list_->ForEach(internal::TestInfoImpl::RunTest);elapsed_time_ = internal::GetTimeInMillis() - start;impl->os_stack_trace_getter()->UponLeavingGTest();// TearDownTestCases事件在這里調(diào)用tear_down_tc_();result_printer->OnTestCaseEnd(this);impl->set_current_test_case(NULL); }

    第二種事件機(jī)制又浮出我們眼前,非常興奮。可以看出,SetUpTestCases和TearDownTestCaess是在一個(gè)TestCase之前和之后調(diào)用的。接著看test_info_list_->ForEach(internal::TestInfoImpl::RunTest):
    static void RunTest(TestInfo * test_info) {
    test_info->impl()->Run();
    }
    哦?TestInfo也有一個(gè)impl?看來(lái)我們之前漏掉了點(diǎn)東西,和UnitTest很類(lèi)似,TestInfo內(nèi)部也有一個(gè)主管各種實(shí)現(xiàn)的類(lèi),那就是TestInfoImpl,它在TestInfo的構(gòu)造函數(shù)中創(chuàng)建了出來(lái)(還記得前面講的TestInfo的創(chuàng)建過(guò)程嗎?):

    TestInfo::TestInfo(const char* test_case_name,const char* name,const char* test_case_comment,const char* comment,internal::TypeId fixture_class_id,internal::TestFactoryBase* factory) {impl_ = new internal::TestInfoImpl(this, test_case_name, name,test_case_comment, comment,fixture_class_id, factory); }

    因此,案例的執(zhí)行還得看TestInfoImpl的Run()方法,同樣,我簡(jiǎn)化一下,只列出關(guān)鍵部分的代碼:

    void TestInfoImpl::Run() {// ...UnitTestEventListenerInterface* const result_printer =impl->result_printer();result_printer->OnTestStart(parent_);// 開(kāi)始計(jì)時(shí)const TimeInMillis start = GetTimeInMillis();Test* test = NULL;__try {// 我們的對(duì)象工廠(chǎng),使用CreateTest()生成Test對(duì)象test = factory_->CreateTest();} __except(internal::UnitTestOptions::GTestShouldProcessSEH(GetExceptionCode())) {AddExceptionThrownFailure(GetExceptionCode(),"the test fixture's constructor");return;}// 如果Test對(duì)象創(chuàng)建成功if (!Test::HasFatalFailure()) {// 調(diào)用Test對(duì)象的Run()方法,執(zhí)行測(cè)試案例 test->Run();}// 執(zhí)行完畢,刪除Test對(duì)象impl->os_stack_trace_getter()->UponLeavingGTest();delete test;test = NULL;// 停止計(jì)時(shí)result_.set_elapsed_time(GetTimeInMillis() - start);result_printer->OnTestEnd(parent_);}

    上面看到了我們前面講到的對(duì)象工廠(chǎng)fatory,通過(guò)fatory的CreateTest()方法,創(chuàng)建Test對(duì)象,然后執(zhí)行案例又是通過(guò)Test對(duì)象的Run()方法:

    void Test::Run() {if (!HasSameFixtureClass()) return;internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();impl->os_stack_trace_getter()->UponLeavingGTest();__try {// Yeah!每個(gè)案例的SetUp事件在這里調(diào)用SetUp();} __except(internal::UnitTestOptions::GTestShouldProcessSEH(GetExceptionCode())) {AddExceptionThrownFailure(GetExceptionCode(), "SetUp()");}// We will run the test only if SetUp() had no fatal failure.if (!HasFatalFailure()) {impl->os_stack_trace_getter()->UponLeavingGTest();__try {// 哈哈!!千辛萬(wàn)苦,我們定義在TEST宏里的東西終于被調(diào)用了!TestBody();} __except(internal::UnitTestOptions::GTestShouldProcessSEH(GetExceptionCode())) {AddExceptionThrownFailure(GetExceptionCode(), "the test body");}}impl->os_stack_trace_getter()->UponLeavingGTest();__try {// 每個(gè)案例的TearDown事件在這里調(diào)用TearDown();} __except(internal::UnitTestOptions::GTestShouldProcessSEH(GetExceptionCode())) {AddExceptionThrownFailure(GetExceptionCode(), "TearDown()");} }

    上面的代碼里非常極其以及特別的興奮的看到了執(zhí)行測(cè)試案例的前后事件,測(cè)試案例執(zhí)行TestBody()的代碼。仿佛整個(gè)gtest的流程在眼前一目了然了。
    四、總結(jié)
    本文通過(guò)分析TEST宏和RUN_ALL_TEST宏,了解到了整個(gè)gtest運(yùn)作過(guò)程,可以說(shuō)整個(gè)過(guò)程簡(jiǎn)潔而優(yōu)美。之前讀《代碼之美》,感觸頗深,現(xiàn)在讀過(guò)gtest代碼,再次讓我感觸深刻。記得很早前,我對(duì)設(shè)計(jì)的理解是“功能越強(qiáng)大越好,設(shè)計(jì)越復(fù)雜越好,那樣才顯得牛”,漸漸得,我才發(fā)現(xiàn),簡(jiǎn)單才是最好。我曾總結(jié)過(guò)自己寫(xiě)代碼的設(shè)計(jì)原則:功能明確,設(shè)計(jì)簡(jiǎn)單。了解了gtest代碼后,猛然發(fā)現(xiàn)gtest不就是這樣嗎,同時(shí)gtest也給了我很多驚喜,因此,我對(duì)gtest的評(píng)價(jià)是:功能強(qiáng)大,設(shè)計(jì)簡(jiǎn)單,使用方便。
    總結(jié)一下gtest里的幾個(gè)關(guān)鍵的對(duì)象:

  • UnitTest 單例,總管整個(gè)測(cè)試,包括測(cè)試環(huán)境信息,當(dāng)前執(zhí)行狀態(tài)等等。
  • UnitTestImpl UnitTest內(nèi)部具體功能的實(shí)現(xiàn)者。
  • Test 我們自己編寫(xiě)的,或通過(guò)TEST,TEST_F等宏展開(kāi)后的Test對(duì)象,管理著測(cè)試案例的前后事件,具體的執(zhí)行代碼TestBody。
  • TestCase 測(cè)試案例對(duì)象,管理著基于TestCase的前后事件,管理內(nèi)部多個(gè)TestInfo。
  • TestInfo 管理著測(cè)試案例的基本信息,包括Test對(duì)象的創(chuàng)建方法。
  • TestInfoImpl TestInfo內(nèi)部具體功能的實(shí)現(xiàn)者 。
    本文還有很多gtest的細(xì)節(jié)沒(méi)有分析到,比如運(yùn)行參數(shù),死亡測(cè)試,跨平臺(tái)處理,斷言的宏等等,希望讀者自己把源碼下載下來(lái)慢慢研究。如本文有錯(cuò)誤之處,也請(qǐng)大家指出,謝謝!
    玩轉(zhuǎn)Google開(kāi)源C++單元測(cè)試框架Google Test系列(gtest)之八 - 打造自己的單元測(cè)試框架
    一、前言
    上一篇我們分析了gtest的一些內(nèi)部實(shí)現(xiàn),總的來(lái)說(shuō)整體的流程并不復(fù)雜。本篇我們就嘗試編寫(xiě)一個(gè)精簡(jiǎn)版本的C++單元測(cè)試框架:nancytest ,通過(guò)編寫(xiě)這個(gè)簡(jiǎn)單的測(cè)試框架,將有助于我們理解gtest。
    二、整體設(shè)計(jì)
    使用最精簡(jiǎn)的設(shè)計(jì),我們就用兩個(gè)類(lèi),夠簡(jiǎn)單吧:
  • TestCase類(lèi)
    包含單個(gè)測(cè)試案例的信息。
  • UnitTest類(lèi)
    負(fù)責(zé)所有測(cè)試案例的執(zhí)行,管理。
    三、TestCase類(lèi)
    TestCase類(lèi)包含一個(gè)測(cè)試案例的基本信息,包括:測(cè)試案例名稱(chēng),測(cè)試案例執(zhí)行結(jié)果,同時(shí)還提供了測(cè)試案例執(zhí)行的方法。我們編寫(xiě)的測(cè)試案例都繼承自TestCase類(lèi)。
  • class TestCase { public:TestCase(const char* case_name) : testcase_name(case_name){}// 執(zhí)行測(cè)試案例的方法virtual void Run() = 0;int nTestResult; // 測(cè)試案例的執(zhí)行結(jié)果 const char* testcase_name; // 測(cè)試案例名稱(chēng) };

    四、UnitTest類(lèi)
    我們的UnitTest類(lèi)和gtest的一樣,是一個(gè)單件。我們的UnitTest類(lèi)的邏輯非常簡(jiǎn)單:

  • 整個(gè)進(jìn)程空間保存一個(gè)UnitTest 的單例。
  • 通過(guò)RegisterTestCase()將測(cè)試案例添加到測(cè)試案例集合testcases_中。
  • 執(zhí)行測(cè)試案例時(shí),調(diào)用UnitTest::Run(),遍歷測(cè)試案例集合testcases_,調(diào)用案例的Run()方法
  • class UnitTest { public:// 獲取單例static UnitTest* GetInstance(); // 注冊(cè)測(cè)試案例TestCase* RegisterTestCase(TestCase* testcase);// 執(zhí)行單元測(cè)試int Run();TestCase* CurrentTestCase; // 記錄當(dāng)前執(zhí)行的測(cè)試案例int nTestResult; // 總的執(zhí)行結(jié)果int nPassed; // 通過(guò)案例數(shù)int nFailed; // 失敗案例數(shù) protected:std::vector<TestCase*> testcases_; // 案例集合 };

    下面是UnitTest類(lèi)的實(shí)現(xiàn):

    UnitTest* UnitTest::GetInstance() {static UnitTest instance;return &instance; }TestCase* UnitTest::RegisterTestCase(TestCase* testcase) {testcases_.push_back(testcase);return testcase; }int UnitTest::Run() {nTestResult = 1;for (std::vector<TestCase*>::iterator it = testcases_.begin();it != testcases_.end(); ++it){TestCase* testcase = *it;CurrentTestCase = testcase;std::cout << green << "======================================" << std::endl;std::cout << green << "Run TestCase:" << testcase->testcase_name << std::endl;testcase->Run();std::cout << green << "End TestCase:" << testcase->testcase_name << std::endl;if (testcase->nTestResult){nPassed++;}else{nFailed++;nTestResult = 0;}}std::cout << green << "======================================" << std::endl;std::cout << green << "Total TestCase : " << nPassed + nFailed << std::endl;std::cout << green << "Passed : " << nPassed << std::endl;std::cout << red << "Failed : " << nFailed << std::endl;return nTestResult; }

    五、NTEST宏
    接下來(lái)定一個(gè)宏NTEST,方便我們寫(xiě)我們的測(cè)試案例的類(lèi)。

    #define TESTCASE_NAME(testcase_name) \testcase_name##_TEST#define NANCY_TEST_(testcase_name) \ class TESTCASE_NAME(testcase_name) : public TestCase \ { \ public: \TESTCASE_NAME(testcase_name)(const char* case_name) : TestCase(case_name){}; \virtual void Run(); \ private: \static TestCase* const testcase_; \ }; \ \ TestCase* const TESTCASE_NAME(testcase_name) \::testcase_ = UnitTest::GetInstance()->RegisterTestCase( \new TESTCASE_NAME(testcase_name)(#testcase_name)); \ void TESTCASE_NAME(testcase_name)::Run()#define NTEST(testcase_name) \NANCY_TEST_(testcase_name)

    六、RUN_ALL_TEST宏
    然后是執(zhí)行所有測(cè)試案例的一個(gè)宏:
    #define RUN_ALL_TESTS()
    UnitTest::GetInstance()->Run();
    七、斷言的宏EXPECT_EQ
    這里,我只寫(xiě)一個(gè)簡(jiǎn)單的EXPECT_EQ :

    #define EXPECT_EQ(m, n)
    if (m != n)
    {
    UnitTest::GetInstance()->CurrentTestCase->nTestResult = 0;
    std::cout << red << “Failed” << std::endl;
    std::cout << red << “Expect:” << m << std::endl;
    std::cout << red << “Actual:” << n << std::endl;
    }

    八、案例Demo
    夠簡(jiǎn)單吧,再來(lái)看看案例怎么寫(xiě):

    #include "nancytest.h"int Foo(int a, int b) {return a + b; }NTEST(FooTest_PassDemo) {EXPECT_EQ(3, Foo(1, 2));EXPECT_EQ(2, Foo(1, 1)); }NTEST(FooTest_FailDemo) {EXPECT_EQ(4, Foo(1, 2));EXPECT_EQ(2, Foo(1, 2)); }int _tmain(int argc, _TCHAR* argv[]) {return RUN_ALL_TESTS(); }

    整個(gè)一山寨版gtest,呵。執(zhí)行一下,看看結(jié)果怎么樣:

    九、總結(jié)
    本篇介紹性的文字比較少,主要是我們?cè)谏弦黄钊虢馕鰃test時(shí)已經(jīng)將整個(gè)流程弄清楚了,而現(xiàn)在編寫(xiě)的nancytest又是其非常的精簡(jiǎn)版本,所有直接看代碼就可以完全理解。希望通過(guò)這個(gè)Demo,能夠讓大家對(duì)gtest有更加直觀的了解。回到開(kāi)篇時(shí)所說(shuō)的,我們沒(méi)有必要每個(gè)人都造一個(gè)輪子,因?yàn)間test已經(jīng)非常出色的為我們做好了這一切。如果我們每個(gè)人都寫(xiě)一個(gè)自己的框架的話(huà),一方面我們要付出大量的維護(hù)成本,一方面,這個(gè)框架也許只能對(duì)你有用,無(wú)法讓大家從中受益。
    gtest正是這么一個(gè)優(yōu)秀C++單元測(cè)試框架,它完全開(kāi)源,允許我們一起為其貢獻(xiàn)力量,并能讓更多人從中受益。如果你在使用gtest過(guò)程中發(fā)現(xiàn)gtest不能滿(mǎn)足你的需求時(shí)(或發(fā)現(xiàn)BUG),gtest的開(kāi)發(fā)人員非常急切的想知道他們哪來(lái)沒(méi)做好,或者是gtest其實(shí)有這個(gè)功能,但是很多用戶(hù)都不知道。所以你可以直接聯(lián)系gtest的開(kāi)發(fā)人員,或者你直接在這里回帖,我會(huì)將您的意見(jiàn)轉(zhuǎn)告給gtest的主要開(kāi)發(fā)人員。
    如果你是gtest的超級(jí)粉絲,原意為gtest貢獻(xiàn)代碼的話(huà),加入他們吧。   
    本Demo代碼下載:/Files/coderzh/Code/nancytest.rar
    本篇是該系列最后一篇,其實(shí)gtest還有更多東西值得我們?nèi)ヌ剿?#xff0c;本系列也不可能將gtest介紹完全,還是那句話(huà),想了解更多gtest相關(guān)的內(nèi)容的話(huà):
    訪(fǎng)問(wèn)官方主頁(yè):http://code.google.com/p/googletest/
    下載gtest源碼: http://code.google.com/p/googletest/downloads/list

    gtest中如何跳出當(dāng)前測(cè)試案例
    在前面的玩轉(zhuǎn)gtest - 斷言中, 我們提到了ASSERT_*系列的斷言只是在當(dāng)前函數(shù)返回,并非退出當(dāng)前測(cè)試案例,因?yàn)锳SSERT_*系列是通過(guò)return來(lái)實(shí)現(xiàn)的(因此 ASSERT_*系列不能在返回值不為void的函數(shù)內(nèi)出現(xiàn))。要退出當(dāng)前測(cè)試案例,一個(gè)最簡(jiǎn)單的方法就是通過(guò)拋異常,然后讓gtest捕獲這一異常。示例如下:

    void Func(int a, int b) {throw "b==0";EXPECT_EQ(0, a\b);printf("End of Func"); }TEST(FooTest, Demo1) {Func(5, 0);printf("End Call Func(5, 0)"); }int _tmain(int argc, _TCHAR* argv[]) {testing::GTEST_FLAG(catch_exceptions) = 1;testing::InitGoogleTest(&argc, argv);return RUN_ALL_TESTS(); }

    (上面的兩個(gè)printf函數(shù)都不會(huì)執(zhí)行。)

    要退出當(dāng)前測(cè)試案例,你只需要兩步:
    1.設(shè)置catch_exception標(biāo)志,在main函數(shù)或是在你的測(cè)試案例前都可以。
    2.要跳出測(cè)試案例時(shí),只需要通過(guò)throw拋出任意異常即可。

    為何通過(guò)這種方法可以跳出當(dāng)前測(cè)試案例,請(qǐng)參考玩轉(zhuǎn)gtest - 深入解析gtest。

    需要注意的是:假如使用的是TEST_F宏,跳出當(dāng)前測(cè)試案例后,會(huì)執(zhí)行TearDown(),因此不必當(dāng)心TearDown中釋放資源的操作不會(huì)執(zhí)行。

    我認(rèn)為一個(gè)好的測(cè)試案例,應(yīng)該是在你的測(cè)試函數(shù)中,比如TEST宏內(nèi),清晰的表達(dá)出你要測(cè)試的對(duì)象,以及預(yù)期的測(cè)試結(jié)果。因此,通常情況下,EXPECT_*和 ASSERT_*應(yīng)該盡量在測(cè)試函數(shù)中出現(xiàn),而不是在測(cè)試函數(shù)內(nèi)調(diào)用的另外函數(shù)或是里面很多層的函數(shù)內(nèi)才出現(xiàn)。(比如上面的Func函數(shù)中的 EXPECT_EQ)。

    編寫(xiě)優(yōu)美的GTest測(cè)試案例
    使用gtest也有很長(zhǎng)一段時(shí)間了,這期間也積累了一些經(jīng)驗(yàn),所以分享一下。GTest為我們提供了便捷的測(cè)試框架,讓我們只需要關(guān)注案例本身。如何在GTest框架下寫(xiě)出優(yōu)美的測(cè)試案例,我覺(jué)得必須要做到:
    1.案例的層次結(jié)構(gòu)一定要清晰
    2.案例的檢查點(diǎn)一定要明確
    3.案例失敗時(shí)一定要能精確的定位問(wèn)題
    4.案例執(zhí)行結(jié)果一定要穩(wěn)定
    5.案例執(zhí)行的時(shí)間一定不能太長(zhǎng)
    6.案例一定不能對(duì)測(cè)試環(huán)境造成破壞
    7.案例一定獨(dú)立,不能與其他案例有先后關(guān)系的依賴(lài)
    8.案例的命名一定清晰,容易理解
    案例的可維護(hù)性也是非常重要,如果做到上面的8點(diǎn),自然也就做到了可維護(hù)性。下面來(lái)分享一下我對(duì)于上面8點(diǎn)的經(jīng)驗(yàn):

  • 案例的層次結(jié)構(gòu)一定要清晰
    所謂層次結(jié)構(gòu),至少要讓人一眼就能分辨出被測(cè)代碼和測(cè)試代碼。簡(jiǎn)單的說(shuō),就是知道你在測(cè)什么。由于是進(jìn)行接口測(cè)試,我已經(jīng)習(xí)慣了如下的案例層次:
  • DataDefine
    我會(huì)將測(cè)試案例所需要的數(shù)據(jù),以及數(shù)據(jù)之間的聯(lián)系全部在預(yù)先定義好。測(cè)試數(shù)據(jù)與案例邏輯的分離,有利于維護(hù)和擴(kuò)展測(cè)試案例。同時(shí),GTest先天就支持測(cè)試數(shù)據(jù)參數(shù)化,為測(cè)試數(shù)據(jù)的分離提供了進(jìn)一步的便捷。什么是測(cè)試數(shù)據(jù)參數(shù)化?就是你可以預(yù)先定義好一批各種各樣的數(shù)據(jù),而你只需要編寫(xiě)一個(gè)測(cè)試案例的邏輯代碼,gtest會(huì)將定義好的數(shù)據(jù)逐個(gè)套入測(cè)試案例中進(jìn)行執(zhí)行。具體的做法請(qǐng)見(jiàn):玩轉(zhuǎn)Google開(kāi)源C++單元測(cè)試框架Google Test系列(gtest)之四 - 參數(shù)化
    SUT
    SUT,即system under test,表明你的測(cè)試對(duì)象是什么,它可以是一個(gè)類(lèi)(CUT),對(duì)象(OUT),函數(shù)(MUT),甚至可以是整個(gè)應(yīng)用程序(AUT)。我單獨(dú)將這個(gè)層次劃分出來(lái),主要有兩個(gè)目的:
    ?明確的表示出你的測(cè)試對(duì)象是什么
    ?為復(fù)雜調(diào)用對(duì)象包裝簡(jiǎn)單調(diào)用接口
    明確表示測(cè)試對(duì)象是什么,便于之后對(duì)測(cè)試案例的維護(hù)和對(duì)測(cè)試案例的理解。同時(shí),對(duì)于一些被測(cè)對(duì)象,你想要調(diào)用它需要經(jīng)過(guò)一系列煩瑣的過(guò)程,這時(shí),就需要將這一煩瑣的調(diào)用過(guò)程隱藏起來(lái),而只關(guān)注被測(cè)對(duì)象的輸入和輸出。
    TestCase
    測(cè)試工程中,必須非常明確的表示出哪些是測(cè)試案例,哪些是其他的輔助文件。通常,我們會(huì)在測(cè)試案例的文件名加上Test前綴(或者后綴)。我建議,將所有的測(cè)試案例文件或代碼放在最顯眼的地方,讓所有看到你的測(cè)試工程的人,第一眼看到的就是測(cè)試案例,這很重要。
    Checker
    對(duì)于一個(gè)復(fù)雜系統(tǒng)的接口測(cè)試,僅僅堅(jiān)持輸入和輸出是遠(yuǎn)遠(yuǎn)不夠的。比如測(cè)試一個(gè)寫(xiě)數(shù)據(jù)庫(kù)的函數(shù),函數(shù)的返回值告訴你數(shù)據(jù)已經(jīng)成功寫(xiě)入是遠(yuǎn)遠(yuǎn)不夠的,你必須親身去數(shù)據(jù)庫(kù)中查個(gè)究竟才行。因此,對(duì)于某一類(lèi)的測(cè)試案例,我們可以抽象出一些通用的檢查點(diǎn)代碼。
    如果做到上面的分層,那么一個(gè)測(cè)試案例寫(xiě)出來(lái)的結(jié)構(gòu)應(yīng)該會(huì)是這個(gè)樣子:

    TEST(TestFoo, JustDemo)
    {
    GetTestData(); // 獲取測(cè)試數(shù)據(jù)

    CallSUT(); // 調(diào)用被測(cè)方法CheckSomething(); // 檢查點(diǎn)驗(yàn)證

    }

    這樣的測(cè)試案例,一目了然。
    2. 案例的檢查點(diǎn)一定要明確
    一定要明確案例的檢查點(diǎn)是什么,并且讓檢查點(diǎn)盡量集中。有一個(gè)不好的習(xí)慣就是核心的檢查點(diǎn)在分布在多個(gè)函數(shù)中,需要不斷的跳轉(zhuǎn)才能了解到這個(gè)案例檢查了些什么。好的做法應(yīng)該是盡量讓檢查點(diǎn)集中,能夠非常清晰的分辨出案例對(duì)被測(cè)代碼做了哪些檢查。所以,盡量讓Gtest的ASSERT_和EXPECT_系列的宏放在明顯和正確的地方。
    3. 案例失敗時(shí)一定要能精確的定位問(wèn)題
    測(cè)試案例失敗時(shí),我們通常手忙腳亂。如果一個(gè)測(cè)試案例Failed,卻不能立即推斷是被測(cè)代碼的Bug的話(huà),這個(gè)測(cè)試案例也有待改進(jìn)。我們可以在一些復(fù)雜的檢查點(diǎn)斷言中加入一些輔助信息,方便我們定位問(wèn)題。比如下面這個(gè)測(cè)試案例:
    int n = -1;
    bool actualResult = Foo::Dosometing(n);
    ASSERT_TRUE(actualResult)
    如果測(cè)試案例失敗了,會(huì)得到下面的信息:
    Value of: actualResult
    Actual: false
    Expected:true
    這樣的結(jié)果對(duì)于我們來(lái)說(shuō),幾乎沒(méi)有什么用。因?yàn)槲覀兏静恢繿ctualResult是什么,以及在什么情況下才會(huì)出現(xiàn)非預(yù)期值。因此,在斷言處多加入一些信息,將有助于定位問(wèn)題:
    int n = -1;
    bool actualResult = Foo::Dosometing(n);
    ASSERT_TRUE(actualResult) << L"Call Foo::Dosometing(n) when n = " << n;
    4. 案例執(zhí)行結(jié)果一定要穩(wěn)定
    要保證測(cè)試案例在什么時(shí)候、什么情況下執(zhí)行的結(jié)果都是一樣的。一個(gè)一會(huì)成功一會(huì)失敗的案例是沒(méi)有意義的。要保證案例穩(wěn)定性的方法有很多,比如杜絕案例之間的影響,有時(shí)候,由于前一個(gè)案例執(zhí)行完后,將一些系統(tǒng)的環(huán)境破壞了,導(dǎo)致后面的案例執(zhí)行失敗。在測(cè)試某些本身就存在一定幾率或延時(shí)的系統(tǒng)時(shí),使用超時(shí)機(jī)制是比較簡(jiǎn)單的辦法。比如,你需要測(cè)試一個(gè)啟動(dòng)Windows服務(wù)的方法,如果我們?cè)谡{(diào)用了該方法后立即進(jìn)行檢查,很可能檢查點(diǎn)會(huì)失敗,有時(shí)候也許又是通過(guò)的。這是因?yàn)閃indows服務(wù)由Stop狀態(tài)到Running狀態(tài),中間還要經(jīng)過(guò)一個(gè)Padding狀態(tài)。所以,簡(jiǎn)單的做法是使用超時(shí)機(jī)制,隔斷時(shí)間檢查一次,直到超過(guò)某個(gè)最大忍受時(shí)間。

    ASSERT_TRUE(StartService('xxx')); int tryTimes = 0; int status = GetServiceStatus('xxx'); while (status != Running) {if (tryTimes >= 10)break;::Sleep(200);tryTimes++;status = GetServiceStatus('xxx'); } ASSERT_EQ(Running, status) << "Check the status after StartService('xxx')";
  • 案例執(zhí)行的時(shí)間一定不能太長(zhǎng)
    我們應(yīng)該盡量讓案例能夠快速的執(zhí)行,一方面,我們可以通過(guò)優(yōu)化我們的代碼來(lái)減少運(yùn)行時(shí)間,比如,減少對(duì)重復(fù)內(nèi)容的讀取。一方面,對(duì)于一些比較耗時(shí)的操作,比如文件系統(tǒng),網(wǎng)絡(luò)操作,我們可以使用Mock對(duì)象來(lái)替代真實(shí)的對(duì)象。使用GMock是一個(gè)不錯(cuò)的選擇。
  • 案例一定不能對(duì)測(cè)試環(huán)境造成破壞
    有的案例需要在特定的環(huán)境下來(lái)能執(zhí)行,因此會(huì)在案例的初始化時(shí)對(duì)環(huán)境進(jìn)行一些修改。注意,不管對(duì)什么東西進(jìn)行了修改,一定要保證在案例執(zhí)行完成的TearDown中將這些環(huán)境都還原回來(lái)。否則有可能對(duì)后面的案例造成影響,或者出現(xiàn)一些莫名其妙的錯(cuò)誤。
  • 案例一定獨(dú)立,不能與其他案例有先后關(guān)系的依賴(lài)
    任何一個(gè)案例都不依賴(lài)于其他測(cè)試案例,任何一個(gè)案例的執(zhí)行結(jié)果都不應(yīng)該影響到別的案例。任何一個(gè)案例都可以單獨(dú)拿出去正確的執(zhí)行。所以,不能寄希望于前一個(gè)案例所做的環(huán)境準(zhǔn)備,因?yàn)檫@是不對(duì)的。
  • 案例的命名一定清晰,容易理解
    案例的名字要規(guī)范,長(zhǎng)不要緊,一定要清晰的表達(dá)測(cè)試案例的用途。比如,下面的測(cè)試案例名稱(chēng)都是不好的:
    TEST(TestFoo, Test)
    TEST(TestFoo, Normal)
    TEST(TestFoo, Alright)
    比如像下面的案例名稱(chēng)就會(huì)好一點(diǎn):
    TEST(TestFoo, Return_True_When_ParameterN_Larger_Then_Zero)
    TEST(TestFoo, Return_False_When_ParameterN_Is_Zero)
  • gtest參數(shù)化測(cè)試代碼示例
    1.在玩轉(zhuǎn) Google開(kāi)源C++單元測(cè)試框架Google Test系列(gtest)之四 - 參數(shù)化中已經(jīng)介紹過(guò)了如何使用gtest進(jìn)行參數(shù)化測(cè)試。在twitter上應(yīng) @xlinker 的要求,我在這里提供一個(gè)參數(shù)化的完整例子。這個(gè)例子也是我當(dāng)初了解gtest時(shí)寫(xiě)的,同時(shí)這個(gè)例子也在《玩轉(zhuǎn)》系列中出現(xiàn)過(guò)。最后,我再附上整個(gè)demo工程,里面有一些其他的示例,剛開(kāi)始上手的同學(xué)可以直接拿我的demo工程去試,有任何疑問(wèn)都?xì)g迎提出。以下是使用TEST_P宏進(jìn)行參數(shù)化測(cè)試的示例:

    #include "stdafx.h" #include "foo.h" #include <gtest/gtest.h>class IsPrimeParamTest : public::testing::TestWithParam<int> {};// 不使用參數(shù)化測(cè)試,就需要像這樣寫(xiě)五次 TEST(IsPrimeTest, HandleTrueReturn) {EXPECT_TRUE(IsPrime(3));EXPECT_TRUE(IsPrime(5));EXPECT_TRUE(IsPrime(11));EXPECT_TRUE(IsPrime(23));EXPECT_TRUE(IsPrime(17)); }// 使用參數(shù)化測(cè)試,只需要: TEST_P(IsPrimeParamTest, HandleTrueReturn) {int n = GetParam();EXPECT_TRUE(IsPrime(n)); }// 定義參數(shù) INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17));// ----------------------- // 更復(fù)雜一點(diǎn)的參數(shù)結(jié)構(gòu)struct NumberPair {NumberPair(int _a, int _b){a = _a;b = _b;}int a;int b; };class FooParamTest : public ::testing::TestWithParam<NumberPair> {};TEST_P(FooParamTest, HandleThreeReturn) {FooCalc foo;NumberPair pair = GetParam();EXPECT_EQ(3, foo.Calc(pair.a, pair.b)); }INSTANTIATE_TEST_CASE_P(ThreeReturn, FooParamTest, testing::Values(NumberPair(12, 15), NumberPair(18, 21)));

    總結(jié)

    以上是生活随笔為你收集整理的玩转Google开源C++单元测试框架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。