Google Test是Google C++ Testing Framework的一種非正式的稱謂,是google最近發布的一個開源C++測試框架。
Google測試框架是在不同平臺上(Linux,Mac OS X,Windows,Cygwin,Windows CE和Symbian)為編寫C++測試而生成的。它是基于xUnit架構的測試框架,支持自動發現測試,豐富的斷言集,用戶定義的斷言,death測試,致命與非致命的失敗,類型參數化測試,各類運行測試的選項和XML的測試報告。
gtest的官方網站是:http://code.google.com/p/googletest/
從官方的使用文檔里,你幾乎可以獲得你想要的所有東西
http://code.google.com/p/googletest/wiki/GoogleTestPrimer
http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide
一、第一個Demo:
先來寫一個被測試函數:
intFoo(int a,int b)


{
if(a==0||b==0)


{
throw "don'tdothat";
}
int c=a%b;
if(c==0)
return b;
return Foo(b,c);
}
上面的函數是用來求最大公約數的。下面我們就來編寫一個簡單的測試案例。
#include<gtest/gtest.h>
TEST(FooTest,HandleNoneZeroInput)
{
EXPECT_EQ(2,Foo(4,10));
EXPECT_EQ(6,Foo(30,18));
}
我們使用了TEST這個宏,它有兩個參數,官方的對這兩個參數的解釋為:[TestCaseName,TestName],而我對這兩個參數的定義是:[TestSuiteName,TestCaseName],在下一篇我們再來看為什么這樣定義。
對檢查點的檢查,我們上面使用到了EXPECT_EQ這個宏,這個宏用來比較兩個數字是否相等。Google還包裝了一系列EXPECT_* 和ASSERT_*的宏,而EXPECT系列和ASSERT系列的區別是:
1. EXPECT_* 失敗時,案例繼續往下執行。
2. ASSERT_* 失敗時,案例終止運行。
為了讓我們的案例運行起來,我們還需要在main函數中添加如下代碼:
int_tmain(intargc,_TCHAR*argv[])


{
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}
“testing::InitGoogleTest(&argc, argv);” :gtest的測試案例允許接收一系列的命令行參數,因此,我們將命令行參數傳遞給gtest,進行一些初始化操作。gtest的命令行參數非常豐富,在后面我們也會詳細了解到。
“RUN_ALL_TESTS()” :運行所有測試案例
二、斷言
gtest中,斷言的宏可以理解為分為兩類,一類是ASSERT系列,一類是EXPECT系列。一個直觀的解釋就是:
1. ASSERT_* 系列的斷言,當檢查點失敗時,退出當前案例的執行。
2. EXPECT_* 系列的斷言,當檢查點失敗時,繼續往下執行。
布爾值檢查
Fatal assertion |
Nonfatal assertion |
Verifies |
ASSERT_TRUE(condition); |
EXPECT_TRUE(condition); |
condition is true |
ASSERT_FALSE(condition); |
EXPECT_FALSE(condition); |
condition is false |
數值型數據檢查
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*同時支持
char*和wchar_t*類型的,*STRCASEEQ*和*STRCASENE*卻只接收char*,估計是不常用吧。下面是幾個例子:
TEST(StringCmpTest,Demo)


{
char*pszCoderZh="CoderZh";
wchar_t*wszCoderZh=L"CoderZh";
std::stringstrCoderZh="CoderZh";
std::wstringwstrCoderZh=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(); |
異常檢查
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 |
例如:
intFoo(inta,intb)


{
if(a==0||b==0)


{
throw"don'tdothat";
}
intc=a%b;
if(c==0)
returnb;
returnFoo(b,c);
}

TEST(FooTest,HandleZeroInput)


{
EXPECT_ANY_THROW(Foo(10,0));
EXPECT_THROW(Foo(0,5),char*);
}
Predicate Assertions
在使用EXPECT_TRUE或ASSERT_TRUE時,有時希望能夠輸出更加詳細的信息,比如檢查一個函數的返回值TRUE還是FALSE時,希望能夠輸出傳入的參數是什么,以便失敗后好跟蹤。因此提供了如下的斷言:
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人說了,他們只提供<=5個參數的,如果需要測試更多的參數,直接告訴他們。下面看看這個東西怎么用。
boolMutuallyPrime(intm,intn)


{
returnFoo(m,n)>1;
}

TEST(PredicateAssertionTest,Demo)


{
intm=5,n=6;
EXPECT_PRED2(MutuallyPrime,m,n);
}
當失敗時,返回錯誤信息:
error: MutuallyPrime(m, n) evaluates to false, where
m evaluates to 5
n evaluates to 6
如果對這樣的輸出不滿意的話,還可以自定義輸出格式,通過如下:
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::AssertionResultAssertFoo(constchar*m_expr,constchar*n_expr,constchar*k_expr,intm,intn,intk)
{
if(Foo(m,n)==k)
returntesting::AssertionSuccess();
testing::Messagemsg;
msg<<m_expr<<"和"<<n_expr<<"的最大公約數應該是:"<<Foo(m,n)<<"而不是:"<<k_expr;
returntesting::AssertionFailure(msg);
}

TEST(AssertFooTest,HandleFail)


{
EXPECT_PRED_FORMAT3(AssertFoo,3,6,2);
}
失敗時,輸出信息:
error: 3 和 6 的最大公約數應該是:3 而不是:2
是不是更溫馨呢,呵呵。
浮點型檢查
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 |
對相近的兩個數比較:
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 |
同時,還可以使用:
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 |
例如:
CComPtrshell;
ASSERT_HRESULT_SUCCEEDED(shell.CoCreateInstance(L"Shell.Application"));
CComVariantempty;
ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url),empty,empty,empty,empty));
類型檢查
類型檢查失敗時,直接導致代碼編不過,難得用處就在這?看下面的例子:
template<typenameT>

classFooType
{
public:
voidBar()


{
testing::StaticAssertTypeEq<int,T>();
}
};

TEST(TypeAssertionTest,Demo)


{
FooType<bool>fooType;
fooType.Bar();
}
三、事件
gtest提供了多種事件機制,非常方便我們在案例之前或之后做一些操作。總結一下gtest的事件一共有3種:
1. 全局的,所有案例執行前后。
2. TestSuite級別的,在某一批案例中第一個案例前,最后一個案例執行后。
3. TestCae級別的,每個TestCase前后。
全局事件
要實現全局事件,必須寫一個類,繼承testing::Environment類,實現里面的SetUp和TearDown方法。
1. SetUp()方法在所有案例執行前執行
2. TearDown()方法在所有案例執行后執行
class FooEnvironment:public testing::Environment


{
public:
virtual void SetUp()


{
std::cout<<"FooFooEnvironmentSetUP"<<std::endl;
}
virtual void TearDown()


{
std::cout<<"FooFooEnvironmentTearDown"<<std::endl;
}
};

當然,這樣還不夠,我們還需要告訴gtest添加這個全局事件,我們需要在main函數中通過testing::AddGlobalTestEnvironment方法將事件掛進來,也就是說,我們可以寫很多個這樣的類,然后將他們的事件都掛上去。
int _tmain(int argc,_TCHAR* argv[])


{
testing::AddGlobalTestEnvironment(newFooEnvironment);
testing::InitGoogleTest(&argc,argv);
return RUN_ALL_TESTS();
}
TestSuite事件
我們需要寫一個類,繼承testing::Test,然后實現兩個靜態方法
1. SetUpTestCase() 方法在第一個TestCase之前執行
2. TearDownTestCase() 方法在最后一個TestCase之后執行

class FooTest:public testing::Test
{
protected:

static void SetUpTestCase()
{
shared_resource_=new;
}

static void TearDownTestCase()
{
delete shared_resource_;
shared_resource_=NULL;
}
//Someexpensiveresourcesharedbyalltests.
staticT* shared_resource_;
};

在編寫測試案例時,我們需要使用TEST_F這個宏,第一個參數必須是我們上面類的名字,代表一個TestSuite。
TEST_F(FooTest,Test1)
{
//youcanrefertoshared_resourcehere
}
TEST_F(FooTest,Test2)
{
//youcanrefertoshared_resourcehere
}
TestCase事件
TestCase事件是掛在每個案例執行前后的,實現方式和上面的幾乎一樣,不過需要實現的是SetUp方法和TearDown方法:
1. SetUp()方法在每個TestCase之前執行
2. TearDown()方法在每個TestCase之后執行
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));
}
四、參數化
在設計測試案例時,經常需要考慮給被測函數傳入不同的值的情況。我們之前的做法通常是寫一個通用方法,然后編寫在測試案例調用它。即使使用了通用方法,這樣的工作也是有很多重復性的,程序員都懶,都希望能夠少寫代碼,多復用代碼。Google的程序員也一樣,他們考慮到了這個問題,并且提供了一個靈活的參數化測試的方案。
舊的方案
為了對比,我還是把舊的方案提一下。首先我先把被測函數IsPrime帖過來(在gtest的example1.cc中),這個函數是用來判斷傳入的數值是否為質數的。
// Returns true iff n is a prime number.
bool IsPrime(int n)


{
// Trivial case 1: small numbers
if (n <= 1) return false;

// Trivial case 2: even numbers
if (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 3

for (int i = 3; ; i += 2)
{
// We only have to try i up to the squre root of n
if (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;
}

假如我要編寫判斷結果為True的測試案例,我需要傳入一系列數值讓函數IsPrime去判斷是否為True(當然,即使傳入再多值也無法確保函數正確,呵呵),因此我需要這樣編寫如下的測試案例:
TEST(IsPrimeTest, HandleTrueReturn)


{
EXPECT_TRUE(IsPrime(3));
EXPECT_TRUE(IsPrime(5));
EXPECT_TRUE(IsPrime(11));
EXPECT_TRUE(IsPrime(23));
EXPECT_TRUE(IsPrime(17));
}
我們注意到,在這個測試案例中,我至少復制粘貼了4次,假如參數有50個,100個,怎么辦?同時,上面的寫法產生的是1個測試案例,里面有5個檢查點,假如我要把5個檢查變成5個單獨的案例,將會更加累人。
接下來,就來看看gtest是如何為我們解決這些問題的。
使用參數化后的方案
1. 告訴gtest你的參數類型是什么
你必須添加一個類,繼承testing::TestWithParam<T>,其中T就是你需要參數化的參數類型,比如上面的例子,我需要參數化一個int型的參數
class IsPrimeParamTest : public::testing::TestWithParam<int>


{

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


{
int n = GetParam();
EXPECT_TRUE(IsPrime(n));
}
嗯,非常的簡潔!
告訴gtest你想要測試的參數范圍是什么
使用INSTANTIATE_TEST_CASE_P這宏來告訴gtest你要測試的參數范圍:
INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17));
第一個參數是測試案例的前綴,可以任意取。
第二個參數是測試案例的名稱,需要和之前定義的參數化的類的名稱相同,如:IsPrimeParamTest
第三個參數是可以理解為參數生成器,上面的例子使用test::Values表示使用括號內的參數。Google提供了一系列的參數生成的函數:
Range(begin, end[, step]) |
范圍在begin~end之間,步長為step,不包括end |
Values(v1, v2, ..., vN) |
v1,v2到vN的值
|
ValuesIn(container) and ValuesIn(begin, end) |
從一個C類型的數組或是STL容器,或是迭代器中取值 |
Bool() |
取false 和 true 兩個值 |
Combine(g1, g2, ..., gN) |
這個比較強悍,它將g1,g2,...gN進行排列組合,g1,g2,...gN本身是一個參數生成器,每次分別從g1,g2,..gN中各取出一個值,組合成一個元組(Tuple)作為一個參數。
說明:這個功能只在提供了<tr1/tuple>頭的系統中有效。gtest會自動去判斷是否支持tr/tuple,如果你的系統確實支持,而gtest判斷錯誤的話,你可以重新定義宏GTEST_HAS_TR1_TUPLE=1。
|
參數化后的測試案例名
因為使用了參數化的方式執行案例,我非常想知道運行案例時,每個案例名稱是如何命名的。
命名規則大概為:
prefix/test_case_name.test.name/index
類型參數化
gtest還提供了應付各種不同類型的數據時的方案,以及參數化類型的方案。我個人感覺這個方案有些復雜。首先要了解一下類型化測試,就用gtest里的例子了。
首先定義一個模版類,繼承testing::Test:
template <typename T>

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

接著我們定義需要測試到的具體數據類型,比如下面定義了需要測試char,int和unsigned int :
typedef testing::Types<char, int, unsigned int> MyTypes;
TYPED_TEST_CASE(FooTest, MyTypes);
又是一個新的宏,來完成我們的測試案例,在聲明模版的數據類型時,使用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);
}

上面的例子看上去也像是類型的參數化,但是還不夠靈活,因為需要事先知道類型的列表。gtest還提供一種更加靈活的類型參數化的方式,允許你在完成測試的邏輯代碼之后再去考慮需要參數化的類型列表,并且還可以重復的使用這個類型列表。下面也是官方的例子:
template <typename T>
class FooTest : public testing::Test {

};
TYPED_TEST_CASE_P(FooTest);
接著又是一個新的宏TYPED_TEST_P類完成我們的測試案例:
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宏,第一個參數是testcase的名稱,后面的參數是test的名稱
REGISTER_TYPED_TEST_CASE_P(FooTest, DoesBlah, HasPropertyA);
接著指定需要的類型列表:
typedef testing::Types<char, int, unsigned int> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);
這種方案相比之前的方案提供更加好的靈活度,當然,框架越靈活,復雜度也會隨之增加。
五、死亡測試
死亡測試”名字比較恐怖,這里的“死亡”指的的是程序的崩潰。通常在測試過程中,我們需要考慮各種各樣的輸入,有的輸入可能直接導致程序崩潰,這時我們就需要檢查程序是否按照預期的方式掛掉,這也就是所謂的“死亡測試”。gtest的死亡測試能做到在一個安全的環境下執行崩潰的測試案例,同時又對崩潰結果進行驗證。
使用的宏
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,用來處理Debug和Realease下的不同。
、*_DEATH(statement, regex`)
1. statement是被測試的代碼語句
2. regex是一個正則表達式,用來匹配異常時在stderr中輸出的內容
如下面的例子:
void Foo()
{
int *pInt = 0;
*pInt = 42 ;
}
TEST(FooDeathTest, Demo)
{
EXPECT_DEATH(Foo(), "");
}
重要:編寫死亡測試案例時,TEST的第一個參數,即testcase_name,請使用DeathTest后綴。原因是gtest會優先運行死亡測試案例,應該是為線程安全考慮。
*_EXIT(statement, predicate, regex`)
1. statement是被測試的代碼語句
2. predicate 在這里必須是一個委托,接收int型參數,并返回bool。只有當返回值為true時,死亡測試案例才算通過。gtest提供了一些常用的predicate:
testing::ExitedWithCode(exit_code)
如果程序正常退出并且退出碼與exit_code相同則返回 true
testing::KilledBySignal(signal_number) // Windows下不支持
如果程序被signal_number信號kill的話就返回true
regex是一個正則表達式,用來匹配異常時在stderr中輸出的內容
這里, 要說明的是,*_DEATH其實是對*_EXIT進行的一次包裝,*_DEATH的predicate判斷進程是否以非0退出碼退出或被一個信號殺死。
例子:
TEST(ExitDeathTest, Demo)
{
EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), "");
}
、*_DEBUG_DEATH
先來看定義:
#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的定義不一樣。因為很多異常只會在Debug版本下拋出,而在Realease版本下不會拋出,所以針對Debug和Release分別做了不同的處理。看gtest里自帶的例子就明白了:
int DieInDebugElse12(int* sideeffect) {
if (sideeffect) *sideeffect = 12;
#ifndef NDEBUG
GTEST_LOG_(FATAL, "debug death inside DieInDebugElse12()");
#endif // NDEBUG
return 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
}
關于正則表達式
在POSIX系統(Linux, Cygwin, 和 Mac)中,gtest的死亡測試中使用的是POSIX風格的正則表達式,想了解POSIX風格表達式可參考:
1. POSIX extended regular expression
2. Wikipedia entry.
在Windows系統中,gtest的死亡測試中使用的是gtest自己實現的簡單的正則表達式語法。 相比POSIX風格,gtest的簡單正則表達式少了很多內容,比如 ("x|y"), ("(xy)"), ("[xy]") 和("x{5,7}")都不支持。
下面是簡單正則表達式支持的一些內容:
|
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定義兩個宏,用來表示當前系統支持哪套正則表達式風格:
1. POSIX風格:GTEST_USES_POSIX_RE = 1
2. Simple風格:GTEST_USES_SIMPLE_RE=1
死亡測試運行方式
1. fast方式(默認的方式)
testing::FLAGS_gtest_death_test_style = "fast";
2. threadsafe方式
testing::FLAGS_gtest_death_test_style = "threadsafe";
你可以在 main() 里為所有的死亡測試設置測試形式,也可以為某次測試單獨設置。Google Test會在每次測試之前保存這個標記并在測試完成后恢復,所以你不需要去管這部分工作 。如:
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();
}
注意事項
1. 不要在死亡測試里釋放內存。
2. 在父進程里再次釋放內存。
3. 不要在程序中使用內存堆檢查。
六、運行參數
使用gtest編寫的測試案例通常本身就是一個可執行文件,因此運行起來非常方便。同時,gtest也為我們提供了一系列的運行參數(環境變量、命令行參數或代碼里指定),使得我們可以對案例的執行進行一些有效的控制。
基本介紹
前面提到,對于運行參數,gtest提供了三種設置的途徑:
1. 系統環境變量
2. 命令行參數
3. 代碼中指定FLAG
因為提供了三種途徑,就會有優先級的問題, 有一個原則是,最后設置的那個會生效。不過總結一下,通常情況下,比較理想的優先級為:
命令行參數 > 代碼中指定FLAG > 系統環境變量
為什么我們編寫的測試案例能夠處理這些命令行參數呢?是因為我們在main函數中,將命令行參數交給了gtest,由gtest來搞定命令行參數的問題。
int _tmain(int argc, _TCHAR* argv[])
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
這樣,我們就擁有了接收和響應gtest命令行參數的能力。如果需要在代碼中指定FLAG,可以使用testing::GTEST_FLAG這個宏來設置。比如相對于命令行參數--gtest_output,可以使用testing::GTEST_FLAG(output) = "xml:";來設置。注意到了,不需要加--gtest前綴了。同時,推薦將這句放置InitGoogleTest之前,這樣就可以使得對于同樣的參數,命令行參數優先級高于代碼中指定。
int _tmain(int argc, _TCHAR* argv[])
{
testing::GTEST_FLAG(output) = "xml:";
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
最后再來說下第一種設置方式-系統環境變量。如果需要gtest的設置系統環境變量,必須注意的是:
1. 系統環境變量全大寫,比如對于--gtest_output,響應的系統環境變量為:GTEST_OUTPUT
2. 有一個命令行參數例外,那就是--gtest_list_tests,它是不接受系統環境變量的。(只是用來羅列測試案例名稱)
參數列表
了解了上面的內容,我這里就直接將所有命令行參數總結和羅列一下。如果想要獲得詳細的命令行說明,直接運行你的案例,輸入命令行參數:/? 或 --help 或 -help
1. 測試案例集合
命令行參數 |
說明 |
--gtest_list_tests |
使用這個參數時,將不會執行里面的測試案例,而是輸出一個案例的列表。
|
--gtest_filter |
對執行的測試案例進行過濾,支持通配符
? 單個字符
* 任意字符
- 排除,如,-a 表示除了a
: 取或,如,a:b 表示a或b
比如下面的例子:
./foo_test 沒有指定過濾條件,運行所有案例 ./foo_test --gtest_filter=* 使用通配符*,表示運行所有案例 ./foo_test --gtest_filter=FooTest.* 運行所有“測試案例名稱(testcase_name)”為FooTest的案例 ./foo_test --gtest_filter=*Null*:*Constructor* 運行所有“測試案例名稱(testcase_name)”或“測試名稱(test_name)”包含Null或Constructor的案例。 ./foo_test --gtest_filter=-*DeathTest.* 運行所有非死亡測試案例。 ./foo_test --gtest_filter=FooTest.*-FooTest.Bar 運行所有“測試案例名稱(testcase_name)”為FooTest的案例,但是除了FooTest.Bar這個案例
|
--gtest_also_run_disabled_tests |
執行案例時,同時也執行被置為無效的測試案例。關于設置測試案例無效的方法為:
在測試案例名稱或測試名稱中添加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] |
設置案例重復運行次數,非常棒的功能!比如:
--gtest_repeat=1000 重復執行1000次,即使中途出現錯誤。 --gtest_repeat=-1 無限次數執行。。。。 --gtest_repeat=1000 --gtest_break_on_failure 重復執行1000次,并且在第一個錯誤發生時立即停止。這個功能對調試非常有用。 --gtest_repeat=1000 --gtest_filter=FooBar 重復執行1000次測試案例名稱為FooBar的案例。
|
測試案例輸出
命令行參數 |
說明 |
--gtest_color=(yes|no|auto) |
輸出命令行時是否使用一些五顏六色的顏色。默認是auto。
|
--gtest_print_time |
輸出命令行時是否打印每個測試案例的執行時間。默認是不打印的。
|
--gtest_output=xml[:DIRECTORY_PATH\|:FILE_PATH] |
將測試結果輸出到一個xml中。
1.--gtest_output=xml: 不指定輸出路徑時,默認為案例當前路徑。
2.--gtest_output=xml:d:\ 指定輸出到某個目錄
3.--gtest_output=xml:d:\foo.xml 指定輸出到d:\foo.xml
如果不是指定了特定的文件路徑,gtest每次輸出的報告不會覆蓋,而會以數字后綴的方式創建。xml的輸出內容后面介紹吧。
|
對案例的異常處理
命令行參數 |
說明 |
--gtest_break_on_failure |
調試模式下,當案例失敗時停止,方便調試
|
--gtest_throw_on_failure |
當案例失敗時以C++異常的方式拋出
|
--gtest_catch_exceptions |
是否捕捉異常。gtest默認是不捕捉異常的,因此假如你的測試案例拋了一個異常,很可能會彈出一個對話框,這非常的不友好,同時也阻礙了測試案例的運行。如果想不彈這個框,可以通過設置這個參數來實現。如將--gtest_catch_exceptions設置為一個非零的數。
注意:這個參數只在Windows下有效。
|
XML報告輸出格式
<?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="3" failures="1" errors="0" time="35" name="AllTests">
<testsuite name="MathTest" tests="2" failures="1"* errors="0" time="15">
<testcase name="Addition" status="run" time="7" classname="">
<failure message="Value of: add(1, 1) Actual: 3 Expected: 2" type=""/>
<failure message="Value of: add(1, -1) Actual: 1 Expected: 0" type=""/>
</testcase>
<testcase name="Subtraction" status="run" time="5" classname="">
</testcase>
</testsuite>
<testsuite name="LogicTest" tests="1" failures="0" errors="0" time="5">
<testcase name="NonContradiction" status="run" time="5" classname="">
</testcase>
</testsuite>
</testsuites>
從報告里可以看出,我們之前在TEST等宏中定義的測試案例名稱(testcase_name)在xml測試報告中其實是一個testsuite name,而宏中的測試名稱(test_name)在xml測試報告中是一個testcase name,概念上似乎有點混淆,就看你怎么看吧。
當檢查點通過時,不會輸出任何檢查點的信息。當檢查點失敗時,會有詳細的失敗信息輸出來failure節點。
在我使用過程中發現一個問題,當我同時設置了--gtest_filter參數時,輸出的xml報告中還是會包含所有測試案例的信息,只不過那些不被執行的測試案例的status值為“notrun”。而我之前認為的輸出的xml報告應該只包含我需要運行的測試案例的信息。不知是否可提供一個只輸出需要執行的測試案例的xml報告。因為當我需要在1000個案例中執行其中1個案例時,在報告中很難找到我運行的那個案例,雖然可以查找,但還是很麻煩。
總結
本篇主要介紹了gtest案例執行時提供的一些參數的使用方法,這些參數都非常有用。在實際編寫gtest測試案例時肯定會需要用到的時候。至少我現在比較常用的就是:
1. --gtest_filter
2. --gtest_output=xml[:DIRECTORY_PATH\|:FILE_PATH]
3. --gtest_catch_exceptions
最后再總結一下我使用過程中遇到的幾個問題:
1. 同時使用--gtest_filter和--gtest_output=xml:時,在xml測試報告中能否只包含過濾后的測試案例的信息。
2. 有時,我在代碼中設置 testing::GTEST_FLAG(catch_exceptions) = 1和我在命令行中使用--gtest_catch_exceptions結果稍有不同,在代碼中設置FLAG方式有時候捕捉不了某些異常,但是通過命令行參數的方式一般都不會有問題。這是我曾經遇到過的一個問題,最后我的處理辦法是既在代碼中設置FLAG,又在命令行參數中傳入--gtest_catch_exceptions。不知道是gtest在catch_exceptions方面不夠穩定,還是我自己測試案例的問題。