2024年2月2日发(作者:)
玩转Google开源C++单元测试框架Google
Test系列(gtest)(总)
前段时间学习和了解了下Google的开源C++单元测试框架Google Test,简称gtest,非常的不错。 我们原来使用的是自己实现的一套单元测试框架,在使用过程中,发现越来越多使用不便之处,而这样不便之处,gtest恰恰很好的解决了。
其实gtest本身的实现并不复杂,我们完全可以模仿gtest,不断的完善我们的测试框架, 但最后我们还是决定使用gtest取代掉原来的自己的测试框架,原因是:
1.不断完善我们的测试框架之后就会发觉相当于把gtest重新做了一遍,虽然轮子造的很爽,但是不是必要的。
2.使用gtest可以免去维护测试框架的麻烦,让我们有更多精力投入到案例设计上。
提高了非常完善的功能,并且简单易用,极大的提高了编写测试案例的效率。
如果想对gtest内部探个究竟,就把它的代码下载下来研究吧,这就是开源的好处,哈!
官方已经有如此完备的文档了,为什么我还要写呢?一方面是自己记记笔记,好记性不如烂笔头,以后自己想查查一些用法也可以直接在这里查到,一方面是对于不想去看一大堆英文文档的朋友,在我这里可以快速的找到gtest相关的内容。
一 、初识gtest
1、 前言
本篇将介绍一些gtest的基本使用,包括下载,安装,编译,建立我们第一个测试Demo工程,以及编写一个最简单的测试案例。
2、 下载
如果不记得网址, 直接在google里搜gtest,第一个就是。目前gtest的最新版本为1.3.0
3、 编译
下载解压后, 里面有个msvc目录:
使用VS的同学可以直接打开msvc里面的工程文件, 如果你在使用的是VS2005或是VS2008,打开后会提示你升级,升完级后,我们直接编译里面的“gtest”工程,可以直接编过的。
这里要提醒一下的是,如果你升级为VS2008的工程,那么你的测试Demo最好也是VS2008
工程,不然你会发现很郁闷,你的Demo怎么也编不过,我也曾折腾了好久,当时我升级为了VS2008工程,结果我使用VS2005工程建Demo,死活编不过。(这里有人误解了,并不是说只能在VS2008中编译,在VS2005中同样可以。如果要编译VS2005版本,最好保证gtest和你的测试工程都使用VS2005工程。)
编译之后,在msvc里面的Debug或是Release目录里看到编译出来的或是文件。
4、 第一个Demo
下面我们开始建立我们的第一个Demo了,假如之前使用的VS2008编译的gtest,那么,我们在VS2008中,新建一个Win32 Console Application。接着就是设置工程属性,总结如下:
1.设置gtest头文件路径
2.设置路径
e Library设置
如果是Release版本,Runtime Library设为/MT。当然,其实你也可以选择动态链接(/MD),前提是你之前编译的gtest也使用了同样是/MD选项。
工程设置后了后,我们来编写一个最简单测试案例试试,我们先来写一个被测试函数:
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);
}
没错,上面的函数是用来求最大公约数的。下面我们就来编写一个简单的测试案例。
#include
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_* 失败时,直接在当前函数中返回,当前函数中ASSERT_*后面的语句将不会执行。
在下一篇,我们再来具体讨论这些断言宏。为了让我们的案例运行起来,我们还需要在main函数中添加如下代码:
int _tmain(int argc, _TCHAR* argv[])
{
testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();
}
“testing::InitGoogleTest(&argc, argv);” :gtest的测试案例允许接收一系列的命令行参数,因此,我们将命令行参数传递给gtest,进行一些初始化操作。gtest的命令行参数非常丰富,在后面我们也会详细了解到。
“RUN_ALL_TESTS()” :运行所有测试案例
OK,一切就绪了,我们直接运行案例试试(一片绿色,非常爽):
5、 总结
本篇内容确实是非常的初级,目的是让从来没有接触过gtest的同学了解gtest最基本的使用。gtest还有很多更高级的使用方法,我们将会在后面讨论。总结本篇的内容的话:
1. 使用VS编译文件
2. 设置测试工程的属性(头文件,lib文件,/MT参数(和编译gtest时使用一样的参数就行了))
3. 使用TEST宏开始一个测试案例,使用EXPECT_*,ASSER_*系列设置检查点。
4. 在Main函数中初始化环境,再使用RUN_ALL_TEST()宏运行测试案例。
优点:
1. 我们的测试案例本身就是一个exe工程,编译之后可以直接运行,非常的方便。
2. 编写测试案例变的非常简单(使用一些简单的宏如TEST),让我们将更多精力花在案例的设计和编写上。
3. 提供了强大丰富的断言的宏,用于对各种不同检查点的检查。
4. 提高了丰富的命令行参数对案例运行进行一系列的设置。
二 、断言
1、 前言
这篇文章主要总结gtest中的所有断言相关的宏。 gtest中,断言的宏可以理解为分为两类,一类是ASSERT系列,一类是EXPECT系列。一个直观的解释就是:
1. ASSERT_* 系列的断言,当检查点失败时,退出当前函数(注意:并非退出当前案例)。
2. EXPECT_* 系列的断言,当检查点失败时,继续往下执行。
2、 示例
// int型比较,预期值:3,实际值:Add(1, 2)EXPECT_EQ(3, Add(1, 2))//
假如你的Add(1, 2) 结果为4的话,会在结果中输出:
g:myprojectc++(16): error: Value of: Add(1, 2)
Actual: 4
Expected:3
如果是将结果输出到xml里的话,将输出:
type=""> Value of: Add(1, 2) Actual: 4 Expected: 3]]>
如果你对自动输出的出错信息不满意的话,你还可以通过操作符<<将一些自定义的信息输
出,通常,这对于调试或是对一些检查点的补充说明来说,非常有用!
下面举个例子:
如果不使用<<操作符自定义输出的话:
for (int i = 0; i < (); ++i)
{
EXPECT_EQ(x[i], y[i]);
}
看到的结果将是这样的,你根本不知道出错时 i 等于几:
g:myprojectc++(25): error: Value of: y[i]
Actual: 4
Expected: x[i]
Which is: 3
如果使用<<操作符将一些重要信息输出的话:
for (int i = 0; i < (); ++i)
{
EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}
从输出结果中就可以定位到在 i = 2 时出现了错误。这样的输出结果看起来更加有用,容易理解:
g:myprojectc++(25): error: Value of: y[i]
Actual: 4
Expected: x[i]
Which is: 3
Vectors x and y differ at index 2
3、 布尔值检查
Fatal assertion
ASSERT_TRUE(condition);
ASSERT_FALSE(condition);
Nonfatal assertion
EXPECT_TRUE(condition);
Verifies
condition is true
EXPECT_FALSE(condition); condition is false
4、 数值型数据检查
Fatal assertion
ASSERT_EQ(expected, actual);
ASSERT_NE(val1, val2);
ASSERT_LT(val1, val2);
ASSERT_LE(val1, val2);
ASSERT_GT(val1, val2);
ASSERT_GE(val1, val2);
Nonfatal assertion
EXPECT_EQ(expected, actual);
EXPECT_NE(val1, val2);
EXPECT_LT(val1, val2);
EXPECT_LE(val1, val2);
EXPECT_GT(val1, val2);
EXPECT_GE(val1, val2);
Verifies
expected == actual
val1 != val2
val1 < val2
val1 <= val2
val1 > val2
val1 >= val2
5、 字符串检查
Fatal assertion Nonfatal assertion Verifies
the two C
strings
ASSERT_STREQ(expected_str,actual_strEXPECT_STREQ(expected_str,actual_strhave the
); );
same
content
the two C
strings
ASSERT_STRNE(str1, str2); EXPECT_STRNE(str1, str2); have
different
content
the two C
strings
have the
ASSERT_STRCASEEQ(expected_str,
actual_str);
EXPECT_STRCASEEQ(expected_str,
same
actual_str);
content,
ignoring
case
the two C
strings
have
ASSERT_STRCASENE(str1,str2); EXPECT_STRCASENE(str1,str2); different
content,
ignoring
case
*STREQ*和*STRNE*同时支持char*和wchar_t*类型的,*STRCASEEQ*和*STRCASENE*却只接收char*,估计是不常用吧。下面是几个例子:
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",
//EXPECT_STRCASEEQ(L"coderzh", wszCoderZh); 不支持
EXPECT_STREQ("CoderZh", strCoderZh.c_str());
EXPECT_STREQ(L"CoderZh", wstrCoderZh.c_str());
}
pszCoderZh);
6、 显示返回成功或失败
直接返回成功:SUCCEED();
返回失败:
Fatal assertion
FAIL();
TEST(ExplicitTest, Demo)
{
ADD_FAILURE() << "Sorry"; // None Fatal Asserton,继续往下执行。 //FAIL(); // Fatal
Assertion,不往下执行该案例。
SUCCEED();
}
Nonfatal assertion
ADD_FAILURE();
7、 异常检查
Fatal assertion Nonfatal assertion Verifies
ASSERT_THROW(statementEXPECT_THROW(statementstatement throws an exception of
,exception_type); ,exception_type); the given type
ASSERT_ANY_THROW(staEXPECT_ANY_THROW(stastatement throws an exception of
tement); tement); any type
ASSERT_NO_THROW(stateEXPECT_NO_THROW(statestatement doesn't throw any
ment);
例如:
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*);
}
ment); exception
8、 Predicate Assertions
在使用EXPECT_TRUE或ASSERT_TRUE时,有时希望能够输出更加详细的信息,比如检查一个函数的返回值TRUE还是FALSE时,希望能够输出传入的参数是什么,以便失败后好跟踪。因此提供了如下的断言:
Fatal assertion
ASSERT_PRED1(pred1, val1);
ASSERT_PRED2(pred2,
val2);
...
Google人说了,他们只提供<=5个参数的,如果需要测试更多的参数,直接告诉他们。下面看看这个东西怎么用。
bool MutuallyPrime(int m, int n)
{ return Foo(m , n) > 1;
}
TEST(PredicateAssertionTest, Demo)
{ int m = 5, n = 6;
EXPECT_PRED2(MutuallyPrime, m, n);
}
当失败时,返回错误信息:
error: MutuallyPrime(m, n) evaluates to false, where
m evaluates to 5
n evaluates to 6
Nonfatal assertion
EXPECT_PRED1(pred1, val1);
Verifies
pred1(val1) returns true
val1, EXPECT_PRED2(pred2,
val2);
...
val1, pred2(val1, val2) returns
true
...
如果对这样的输出不满意的话,还可以自定义输出格式,通过如下:
Fatal assertion Nonfatal assertion Verifies
pred_format1(val1) is successful
pred_format2(val1ASSERT_PRED_FORMAT2(pred_foEXPECT_PRED_FORMAT2(pred_f,
rmat2, val1, val2); ormat2, val1, val2);
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 << " 的最大公约数应该是:" << Foo(m, n) << " 而不是:" << k_expr; return testing::AssertionFailure(msg);
}
TEST(AssertFooTest, HandleFail)
{
EXPECT_PRED_FORMAT3(AssertFoo, 3, 6, 2);
}
失败时,输出信息:
error: 3 和 6 的最大公约数应该是:3 而不是:2
是不是更温馨呢,呵呵。
...
val2) is
ASSERT_PRED_FORMAT1(pred_foEXPECT_PRED_FORMAT1(pred_frmat1, val1);` ormat1, val1);
9、 浮点型检查
Fatal assertion Nonfatal assertion Verifies
the two float
ASSERT_FLOAT_EQ(expected,
actual);
EXPECT_FLOAT_EQ(expected,
values
actual);
almost equal
the two double
ASSERT_DOUBLE_EQ(expected,
actual);
EXPECT_DOUBLE_EQ(expected,
values
actual);
almost equal
对相近的两个数比较:
Fatal assertion Nonfatal assertion Verifies
the difference between val1 and
ASSERT_NEAR(val1, val2, EXPECT_NEAR(val1, val2,
val2 doesn't exceed the given
abs_error); abs_error);
absolute error
同时,还可以使用:
EXPECT_PRED_FORMAT2(testing::FloatLE, val1, val2);
EXPECT_PRED_FORMAT2(testing::DoubleLE, val1, val2);
are
are
10、 Windows HRESULT assertions
Fatal assertion Nonfatal assertion Verifies
expression is a ASSERT_HRESULT_SUCCEEDEDEXPECT_HRESULT_SUCCEEDE(expression); D(expression); success HRESULT
is a ASSERT_HRESULT_FAILED(expreEXPECT_HRESULT_FAILED(exprexpression
ssion);
例如:
CComPtr shell;
ession); failure HRESULT
ASSERT_HRESULT_SUCCEEDED(teInstance(L"ation"));
CComVariant empty;
ASSERT_HRESULT_SUCCEEDED(shell->ShellExecute(CComBSTR(url), empty, empty, empty,
empty));
11、 类型检查
类型检查失败时,直接导致代码编不过,难得用处就在这?看下面的例子:
template T>(); } }; TEST(TypeAssertionTest, Demo) { FooType (); } 12、 总结 本篇将常用的断言都介绍了一遍,内容比较多,有些还是很有用的。要真的到写案例的时候,也行只是一两种是最常用的,现在时知道有这么多种选择,以后才方便查询。 三、事件机制 1、 前言 gtest提供了多种事件机制,非常方便我们在案例之前或之后做一些操作。总结一下gtest的事件一共有3种: 1. 全局的,所有案例执行前后。 2. TestSuite级别的,在某一批案例中第一个案例前,最后一个案例执行后。 3. TestCase级别的,每个TestCase前后。 2、 全局事件 要实现全局事件,必须写一个类,继承testing::Environment类,实现里面的SetUp和TearDown方法。 1. SetUp()方法在所有案例执行前执行 2. TearDown()方法在所有案例执行后执行 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; } }; 当然,这样还不够,我们还需要告诉gtest添加这个全局事件,我们需要在main函数中通过 testing::AddGlobalTestEnvironment方法将事件挂进来,也就是说,我们可以写很多个这样的类,然后将他们的事件都挂上去。 int _tmain(int argc, _TCHAR* argv[]) { testing::AddGlobalTestEnvironment(new FooEnvironment); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } 3、 TestSuite事件 我们需要写一个类,继承testing::Test,然后实现两个静态方法 1. SetUpTestCase() 方法在第一个TestCase之前执行 wnTestCase() 方法在最后一个TestCase之后执行 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_; }; 在编写测试案例时,我们需要使用TEST_F这个宏,第一个参数必须是我们上面类的名字,代表一个TestSuite。 TEST_F(FooTest, Test1) { // you can refer to shared_resource here TEST_F(FooTest, Test2) { // you can refer to shared_resource here } } 4、 TestCase事件 TestCase事件是挂在每个案例执行前后的,实现方式和上面的几乎一样,不过需要实现的是SetUp方法和TearDown方法: 1. SetUp()方法在每个TestCase之前执行 2. TearDown()方法在每个TestCase之后执行 class FooCalcTest:public testing::Test {protected: virtual void SetUp() { m_(); } virtual void TearDown() { m_ze(); } FooCalc m_foo; }; TEST_F(FooCalcTest, HandleNoneZeroInput) { EXPECT_EQ(4, m_(12, 16)); } TEST_F(FooCalcTest, HandleNoneZeroInput_Error) { EXPECT_EQ(5, m_(12, 16)); } 5、 总结 gtest提供的这三种事件机制还是非常的简单和灵活的。同时,通过继承Test类,使用TEST_F宏,我们可以在案例之间共享一些通用方法,共享资源。使得我们的案例更加的简洁,清晰。 四、参数化 1、 前言 在设计测试案例时,经常需要考虑给被测函数传入不同的值的情况。我们之前的做法通常是写一个通用方法,然后编写在测试案例调用它。即使使用了通用方法,这样的工作也是有很多重复性的,程序员都懒,都希望能够少写代码,多复用代码。Google的程序员也一样,他们考虑到了这个问题,并且提供了一个灵活的参数化测试的方案。 2、 旧的方案 为了对比,我还是把旧的方案提一下。首先我先把被测函数IsPrime帖过来(在gtest的中),这个函数是用来判断传入的数值是否为质数的。 // Returns true iff n is a prime 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是如何为我们解决这些问题的。 3、 使用参数化后的方案 1. 告诉gtest你的参数类型是什么 你必须添加一个类,继承testing::TestWithParam class IsPrimeParamTest : public::testing::TestWithParam { }; 2. 告诉gtest你拿到参数的值后,具体做些什么样的测试 这里,我们要使用一个新的宏(嗯,挺兴奋的):TEST_P,关于这个"P"的含义,Google给出的答案非常幽默,就是说你可以理解为”parameterized" 或者 "pattern"。我更倾向于 ”parameterized"的解释,呵呵。在TEST_P宏里,使用GetParam()获取当前的参数的具体值。 TEST_P(IsPrimeParamTest, HandleTrueReturn) { int n = GetParam(); EXPECT_TRUE(IsPrime(n)); } 嗯,非常的简洁! 3. 告诉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]) Values(v1, v2, ..., vN) ValuesIn(container) 从一个C类型的数组或是STL容器,或是迭代器中取值 andValuesIn(begin, end) Bool() 取false 和 true 两个值 这个比较强悍,它将g1,g2,...gN进行排列组合,g1,g2,...gN本身是一个参数生成器,每次分别从g1,g2,..gN中各取出一个值,组合成一个元组(Tuple)作为一个参数。 Combine(g1, g2, ..., gN) 说明:这个功能只在提供了 v1,v2到vN的值 GTEST_HAS_TR1_TUPLE=1。 4、 参数化后的测试案例名 因为使用了参数化的方式执行案例,我非常想知道运行案例时,每个案例名称是如何命名的。我执行了上面的代码,输出如下: 从上面的框框中的案例名称大概能够看出案例的命名规则,对于需要了解每个案例的名称的我来说,这非常重要。 命名规则大概为: prefix/test_case_/index 5、 类型参数化 gtest还提供了应付各种不同类型的数据时的方案,以及参数化类型的方案。我个人感觉这个方案有些复杂。首先要了解一下类型化测试,就用gtest里的例子了。 首先定义一个模版类,继承testing::Test: template typedef std::list T value_; }; 接着我们定义需要测试到的具体数据类型,比如下面定义了需要测试char,int和unsigned int : typedef testing::Types 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; _back(n); } 上面的例子看上去也像是类型的参数化,但是还不够灵活,因为需要事先知道类型的列表。gtest还提供一种更加灵活的类型参数化的方式,允许你在完成测试的逻辑代码之后再去考虑需要参数化的类型列表,并且还可以重复的使用这个类型列表。下面也是官方的例子: template }; 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 INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); 这种方案相比之前的方案提供更加好的灵活度,当然,框架越灵活,复杂度也会随之增加。 } 6、 测试代码示例 在twitter上应 @xlinker 的要求,我在这里提供一个参数化的完整例子。这个例子也是我当初了解gtest时写的,同时这个例子也在《玩转》系列中出现过。最后,我再附上整个demo工程,里面有一些其他的示例,刚开始上手的同学可以直接拿我的demo工程去试,有任何疑问都欢迎提出。以下是使用TEST_P宏进行参数化测试的示例: #include "stdafx.h" #include "foo.h" #include { };// 不使用参数化测试,就需要像这样写五次TEST(IsPrimeTest, HandleTrueReturn) { EXPECT_TRUE(IsPrime(3)); EXPECT_TRUE(IsPrime(5)); EXPECT_TRUE(IsPrime(11)); EXPECT_TRUE(IsPrime(23)); EXPECT_TRUE(IsPrime(17)); }// 使用参数化测试,只需要:TEST_P(IsPrimeParamTest, HandleTrueReturn) { int n = GetParam(); EXPECT_TRUE(IsPrime(n)); } // 定义参数 INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17)); // -----------------------// 更复杂一点的参数结构struct NumberPair { NumberPair(int _a, int _b) { a = _a; b = _b; } int a; int b; };class FooParamTest : public ::testing::TestWithParam { }; TEST_P(FooParamTest, HandleThreeReturn) { FooCalc foo; NumberPair pair = GetParam(); EXPECT_EQ(3, (pair.a, pair.b)); } INSTANTIATE_TEST_CASE_P(ThreeReturn, FooParamTest, testing::Values(NumberPair(12, 15), NumberPair(18, 21))); 7、 总结 gtest为我们提供的参数化测试的功能给我们的测试带来了极大的方便,使得我们可以写更少更优美的代码,完成多种参数类型的测试案例。 五、死亡测试 1、 前言 “死亡测试”名字比较恐怖,这里的“死亡”指的的是程序的崩溃。通常在测试过程中,我们需要考虑各种各样的输入,有的输入可能直接导致程序崩溃,这时我们就需要检查程序是否按照预期的方式挂掉,这也就是所谓的“死亡测试”。gtest的死亡测试能做到在一个安全的环境下执行崩溃的测试案例,同时又对崩溃结果进行验证。 2、 使用的宏 Fatal assertion ASSERT_DEATH(statement, regex`); Nonfatal assertion EXPECT_DEATH(statement, regex`); Verifies statement crashes with the given error statement exits with the ASSERT_EXIT(statement, predicate, regex`); EXPECT_EXIT(statement, given error and its exit predicate, regex`); code matches predicate 由于有些异常只在Debug下抛出,因此还提供了*_DEBUG_DEATH,用来处理Debug和Realease下的不同。 3、 *_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会优先运行死亡测试案例,应该是为线程安全考虑。 4、 *_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 3. regex是一个正则表达式,用来匹配异常时在stderr中输出的内容 这里, 要说明的是,*_DEATH其实是对*_EXIT进行的一次包装,*_DEATH的predicate判断进程是否以非0退出码退出或被一个信号杀死。 例子: TEST(ExitDeathTest, Demo) { EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), ""); } 5、 *_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 } 6、 关于正则表达式 在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}")都不支持。 下面是简单正则表达式支持的一些内容: d D f n r s S t v w matches any literal character c matches any decimal digit matches any character that's not a decimal digit matches f matches n matches r matches any ASCII whitespace, including n matches any character that's not a whitespace matches t matches v matches any letter, _, or decimal digit W matches any character that w doesn't match c . A? A* A+ ^ $ xy matches any literal character c, which must be a punctuation matches any single character except n matches 0 or 1 occurrences of A matches 0 or many occurrences of 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) matches x followed by y gtest定义两个宏,用来表示当前系统支持哪套正则表达式风格: 1. POSIX风格:GTEST_USES_POSIX_RE = 1 2. Simple风格:GTEST_USES_SIMPLE_RE=1 7、 死亡测试运行方式 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(); } 8、 注意事项 1. 不要在死亡测试里释放内存。 2. 在父进程里再次释放内存。 3. 不要在程序中使用内存堆检查。 9、 总结 关于死亡测试,gtest官方的文档已经很详细了,同时在源码中也有大量的示例。如想了解更多的请参考官方的文档,或是直接看gtest源码。 简单来说,通过*_DEATH(statement, regex`)和*_EXIT(statement, predicate, regex`),我们可以非常方便的编写导致崩溃的测试案例,并且在不影响其他案例执行的情况下,对崩溃案例的结果进行检查。 六、运行参数 1、 前言 使用gtest编写的测试案例通常本身就是一个可执行文件,因此运行起来非常方便。同时,gtest也为我们提供了一系列的运行参数(环境变量、命令行参数或代码里指定),使得我们可以对案例的执行进行一些有效的控制。 2、 基本介绍 前面提到,对于运行参数,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,它是不接受系统环境变量的。(只是用来罗列测试案例名称) 3、 参数列表 了解了上面的内容,我这里就直接将所有命令行参数总结和罗列一下。如果想要获得详细的命令行说明,直接运行你的案例,输入命令行参数:/? 或 --help 或 -help 1. 测试案例集合 命令行参数 --gtest_list_tests 说明 使用这个参数时,将不会执行里面的测试案例,而是输出一个案例的列表。对执行的测试案例进行过滤,支持通配符 ? 单个字符 * 任意字符 - 排除,如,-a 表示除了a --gtest_filter : 取或,如,a:b 表示a或b 比如下面的例子: ./foo_test 没有指定过滤条件,运行所有案例 ./foo_test --gtest_filter=* 使用通配符*,表示运行所有案例 ./foo_test --gtest_filter=FooTest.* 运行所有“测试案例名称(testcase_name)”为./foo_test --gtest_filter=*Null*:*Constructor* 运行所有“测试案例名称(tConstructor的案例。 ./foo_test --gtest_filter=-*DeathTest.* 运行所有非死亡测试案例。 ./foo_test --gtest_filter=FooTest.*- 运行所有“测试案例名称(testca案例 执行案例时,同时也执行被置为无效的测试案例。关于设置测试案例无效的在测试案例名称或测试名称中添加DISABLED前缀,比如: // Tests that Foo does Abc. TEST(FooTest, DISABLED_DoesAbc) { --gtest_also_run_disabled_tests class DISABLED_BarTest : public testing::Test { // Tests that Bar does Xyz. TEST_F(DISABLED_BarTest, DoesXyz) { 设置案例重复运行次数,非常棒的功能!比如: --gtest_repeat=1000 重复执行1000次,即使中途出现错误。 --gtest_repeat=-1 无限次数执行。。。。 --gtest_repeat=[COUNT] } }; } --gtest_repeat=1000 --gtest_break_on_failure 重复执行1000次,并且在第--gtest_repeat=1000 --gtest_filter=FooBar 重复执行1000次测试案例名称 2. 测试案例输出 命令行参数 说明 --gtest_color=(yes|no|auto) --gtest_print_time 输出命令行时是否使用一些五颜六色的颜色。默认是输出命令行时是否打印每个测试案例的执行时间。默将测试结果输出到一个xml中。 1.--gtest_output=xml: 不指定输出路径时,默认为 2.--gtest_output=xml:d: 指定输出到某个目录 --gtest_output=xml[:DIRECTORY_PATH|:FILE_PATH] 3.--gtest_output=xml:d: 指定输出到d:如果不是指定了特定的文件路径,gtest每次输出的报内容后面介绍吧。 3. 对案例的异常处理 命令行参数 --gtest_break_on_failure --gtest_throw_on_failure 说明 调试模式下,当案例失败时停止,方便调试 当案例失败时以C++异常的方式抛出 是否捕捉异常。gtest默认是不捕捉异常的,因此假如你的测试案例抛了一个异常--gtest_catch_exceptions 注意:这个参数只在Windows下有效。 阻碍了测试案例的运行。如果想不弹这个框,可以通过设置这个参数来实现。如将4、 XML报告输出格式 name="AllTests"> time="5" classname=""> failures="0" errors="0" time="5"> time="5" classname=""> 从报告里可以看出,我们之前在TEST等宏中定义的测试案例名称(testcase_name)在xml测试报告中其实是一个testsuite name,而宏中的测试名称(test_name)在xml测试报告中是一个testcase name,概念上似乎有点混淆,就看你怎么看吧。 当检查点通过时,不会输出任何检查点的信息。当检查点失败时,会有详细的失败信息输出来failure节点。 在我使用过程中发现一个问题,当我同时设置了--gtest_filter参数时,输出的xml报告中还是会包含所有测试案例的信息,只不过那些不被执行的测试案例的status值为“notrun”。而我之前认为的输出的xml报告应该只包含我需要运行的测试案例的信息。不知是否可提供一个只输出需要执行的测试案例的xml报告。因为当我需要在1000个案例中执行其中1个案例时,在报告中很难找到我运行的那个案例,虽然可以查找,但还是很麻烦。 5、 总结 本篇主要介绍了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方面不够稳定,还是我自己测试案例的问题。 七、深入解析gtest 1、 前言 “深入解析”对我来说的确有些难度,所以我尽量将我学习到和观察到的gtest内部实现介绍给大家。本文算是抛砖引玉吧,只能是对gtest的整体结构的一些介绍,想要了解更多细节最好的办法还是看gtest源码,如果你看过gtest源码,你会发现里面的注释非常的详细!好了,下面就开始了解gtest吧。 2、 从TEST宏开始 前面的文章已经介绍过TEST宏的用法了,通过TEST宏,我们可以非法简单、方便的编写测试案例,比如: TEST(FooTest, Demo) { EXPECT_EQ(1, 1); } 我们先不去看TEST宏的定义,而是先使用/P参数将TEST展开。如果使用的是Vistual Studio的话: 1. 选中需要展开的代码文件,右键 - 属性 - C/C++ - Preprocessor 2. Generate Preprocessed File 设置 Without Line Numbers (/EP /P) 或 With Line Numbers (/P) 3. 关闭属性对话框,右键选中需要展开的文件,右键菜单中点击:Compile 编译过后,会在源代码目录生成一个后缀为.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 &); }; ::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 &); void operator=(const 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_", 9, gtest_e_message() ) = ::testing::Message(); } 展开后,我们观察到: 1. TEST宏展开后,是一个继承自testing::Test的类。 2. 我们在TEST宏里面写的测试代码,其实是被放到了类的TestBody方法中。 3. 通过静态变量test_info_,调用MakeAndRegisterTestInfo对测试案例进行注册。 如下图: 上面关键的方法就是MakeAndRegisterTestInfo了,我们跳到MakeAndRegisterTestInfo函数中: // 创建一个 TestInfo 对象并注册到 Google Test;// 返回创建的TestInfo对象//// 参数://// test_case_name: 测试案例的名称// name: 测试的名称// test_case_comment: 测试案例的注释信息// comment: 测试的注释信息// fixture_class_id: test fixture类的ID// set_up_tc: 事件函数SetUpTestCases的函数地址// tear_down_tc: 事件函数TearDownTestCases的函数地址// factory: 工厂对象,用于创建测试对象(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; } 我们看到,上面创建了一个TestInfo对象,然后通过AddTestInfo注册了这个对象。TestInfo对象到底是一个什么样的东西呢? TestInfo对象主要用于包含如下信息: 1. 测试案例名称(testcase name) 2. 测试名称(test name) 3. 该案例是否需要执行 4. 执行案例时,用于创建Test对象的函数指针 5. 测试结果 我们还看到,TestInfo的构造函数中,非常重要的一个参数就是工厂对象,它主要负责在运行测试案例时创建出Test对象。我们看到我们上面的例子的factory为: new ::testing::internal::TestFactoryImpl< FooTest_Demo_Test> 我们明白了,Test对象原来就是TEST宏展开后的那个类的对象(FooTest_Demo_Test),再看看TestFactoryImpl的实现: template Test* CreateTest() { return new TestClass; } }; 这个对象工厂够简单吧,嗯,Simple is better。当我们需要创建一个测试对象(Test)时,调用factory的CreateTest()方法就可以了。 创建了TestInfo对象后,再通过下面的方法对TestInfo对象进行注册: GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); GetUnitTestImpl()是获取UnitTestImpl对象: inline UnitTestImpl* GetUnitTestImpl() { return UnitTest::GetInstance()->impl(); } 其中UnitTest是一个单件(Singleton),整个进程空间只有一个实例,通过UnitTest::GetInstance()获取单件的实例。上面的代码看到,UnitTestImpl对象是最终是从UnitTest对象中获取的。那么UnitTestImpl到底是一个什么样的东西呢?可以这样理解: UnitTestImpl是一个在UnitTest内部使用的,为执行单元测试案例而提供了一系列实现的那么一个类。(自己归纳的,可能不准确) 我们上面的AddTestInfo就是其中的一个实现,负责注册TestInfo实例: // 添加TestInfo对象到整个单元测试中//// 参数://// set_up_tc: 事件函数SetUpTestCases的函数地址 // tear_down_tc: 事件函数TearDownTestCases的函数地址 // test_info: TestInfo对象void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, Test::TearDownTestCaseFunc tear_down_tc, TestInfo * test_info) {// 处理死亡测试的代码,先不关注它if (original_working_dir_.IsEmpty()) { original_working_dir_.Set(FilePath::GetCurrentDir()); if (original_working_dir_.IsEmpty()) { printf("%sn", "Failed to get the current working directory."); abort(); } }// 获取或创建了一个TestCase对象,并将testinfo添加到TestCase对象中。GetTestCase(test_info->test_case_name(), test_info->test_case_comment(), set_up_tc, tear_down_tc)->AddTestInfo(test_info); } 我们看到,TestCase对象出来了,并通过AddTestInfo添加了一个TestInfo对象。这时,似乎豁然开朗了: 1. TEST宏中的两个参数,第一个参数testcase_name,就是TestCase对象的名称,第二个参数test_name就是Test对象的名称。而TestInfo包含了一个测试案例的一系列信息。 2. 一个TestCase对象对应一个或多个TestInfo对象。 我们来看看TestCase的创建过程(UnitTestImpl::GetTestCase): // 查找并返回一个指定名称的TestCase对象。如果对象不存在,则创建一个并返回//// 参数://// test_case_name: 测试案例名称// set_up_tc: 事件函数SetUpTestCases的函数地址// tear_down_tc: 事件函数TearDownTestCases的函数地址TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, const char* comment, Test::SetUpTestCaseFunc set_up_tc, Test::TearDownTestCaseFunc tear_down_tc) { // 从test_cases里查找指定名称的TestCase internal::ListNode test_cases_.FindIf( TestCaseNameIs(test_case_name)); if (node == NULL) { // 没找到,我们来创建一个 TestCase* const test_case = new TestCase(test_case_name, comment, set_up_tc, tear_down_tc); // 判断是否为死亡测试案例 if (internal::UnitTestOptions::MatchesFilter(String(test_case_name), kDeathTestCaseFilter)) { // 是的话,将该案例插入到最后一个死亡测试案例后 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对象 return node->element(); } 3、 回过头看看TEST宏的定义 #define TEST(test_case_name, test_name) GTEST_TEST_(test_case_name, test_name, ::testing::Test, ::testing::internal::GetTestTypeId()) 同时也看看TEST_F宏 #define TEST_F(test_fixture, test_name) GTEST_TEST_(test_fixture, test_name, test_fixture, ::testing::internal::GetTypeId 都是使用了GTEST_TEST_宏,在看看这个宏如何定义的: #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, GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() 不需要多解释了,和我们上面展开看到的差不多,不过这里比较明确的看到了,我们在TEST宏里写的就是TestBody里的东西。这里再补充说明一下里面的GTEST_DISALLOW_COPY_AND_ASSIGN_宏,我们上面的例子看出,这个宏展开后: FooTest_Demo_Test(const FooTest_Demo_Test &);void operator=(const FooTest_Demo_Test &); test_name)>);void 正如这个宏的名字一样,它是用于防止对对象进行拷贝和赋值操作的。 4、 再来了解RUN_ALL_TESTS宏 我们的测试案例的运行就是通过这个宏发起的。RUN_ALL_TEST的定义非常简单: #define RUN_ALL_TESTS() (::testing::UnitTest::GetInstance()->Run()) 我们又看到了熟悉的::testing::UnitTest::GetInstance(),看来案例的执行时从UnitTest的Run方法开始的,我提取了一些Run中的关键代码,如下: int UnitTest::Run() { __try { return impl_->RunAllTests(); } __except(internal::UnitTestOptions::GTestShouldProcessSEH( GetExceptionCode())) { printf("Exception thrown with code 0x%", GetExceptionCode()); fflush(stdout); return 1; } return impl_->RunAllTests(); } 我们又看到了熟悉的impl(UnitTestImpl),具体案例该怎么执行,还是得靠UnitTestImpl。 int UnitTestImpl::RunAllTests() { // ... printer->OnUnitTestStart(parent_); // 计时 const TimeInMillis start = GetTimeInMillis();


发布评论