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方面不够稳定,还是我自己测试案例的问题。