gtest单元测试

此贴是是从整理过来的,自己看的方便。原帖地址:

http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html

我会在工作中,对自己的使用感受和用法,对文章进行不断的修改
================================我是华丽的分割线================================

使用体会:

1、对一系列的类或函数写测试用例的时候,可以使用TestSuite方式,重写SetUpTestCase和TearDownTestCase函数,来提供共用的测试环境。

2、测试用例需要读文件之类的话,可以尝试测试用例中自动写一份测试文件,这样就不用测试时再拷入一堆环境。需要的DLL嵌入EXE,运行时释放更方便呢?应该还是看测试的方便程度来衡量吧?我感觉是这样的。

3、形式主义,这是最大的问题。由于我们的项目刚刚加入单元测试,但是视乎大家都不是很重视,最郁闷的是,我发现它变成了一个形式主义,摆在那面。没有真正的意义。还不明白,到底应该如何正确的运用它!!!

================================我是华丽的分割线================================

gtest的官方网站是:
http://code.google.com/p/googletest/

从官方的使用文档里,你几乎可以获得你想要的所有东西

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

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

================================我是华丽的分割线================================

一、下载,编译,建立Demo工程

1、下载gtest

http://code.google.com/p/googletest/downloads/list

2、编译

下载解压后, 里面有个msvc目录:

 

使用VS直接编译msvc里面的gtest工程文件。编译之后,在msvc里面的Debug或是Release目录里看到编译出来的gtestd.lib或是gtest.lib文件。

 

3、建立工程

新建一个Win32 Console Application。接着就是设置工程属性,总结如下:(别忘记gtest的文件)

(1).设置gtest头文件路径

(2).设置gtest.lib路径

(3).Runtime Library设置

 

如果是Release版本,Runtime Library设为/MT。当然,其实你也可以选择动态链接(/MD),前提是你之前编译的gtest也使用了同样是/MD选项。

 

4、Demo

[cpp]  view plain copy
  1. int Foo(int a, int b)  
  2. {  
  3.     if (a == 0 || b == 0)  
  4.     {  
  5.         throw "don't do that";  
  6.     }  
  7.     int c = a % b;  
  8.     if (c == 0)  
  9.         return b;  
  10.     return Foo(b, c);  
  11. }  

被测试函数,求最大公约数。

[cpp]  view plain copy
  1. #include <gtest/gtest.h>  
  2.   
  3. TEST(FooTest, HandleNoneZeroInput)  
  4. {  
  5.     EXPECT_EQ(2, Foo(4, 10));  
  6.     EXPECT_EQ(6, Foo(30, 18));  
  7. }  

使用了TEST这个宏,它有两个参数,官方的对这两个参数的解释为:[TestCaseName,TestName],而我对这两个参数的定义是:[TestSuiteName,TestCaseName]。

EXPECT系列宏和ASSERT系列宏的区别是:

    1. EXPECT_*  失败时,案例继续往下执行。

    2. ASSERT_* 失败时,直接在当前函数中返回,当前函数中ASSERT_*后面的语句将不会执行。 

main函数中添加如下代码:

[cpp]  view plain copy
  1. int _tmain(int argc, _TCHAR* argv[])  
  2. {  
  3.     testing::InitGoogleTest(&argc, argv);  
  4.     return RUN_ALL_TESTS();//运行所有测试案例  
  5. }  

OK,一切就绪了,我们直接运行案例试试(一片绿色,非常爽):

 

 
5、总结

    1. 使用VS编译gtest.lib文件

    2. 设置测试工程的属性(头文件,lib文件,/MT参数(和编译gtest时使用一样的参数就行了))

    3. 使用TEST宏开始一个测试案例,使用EXPECT_*,ASSER_*系列设置检查点。

    4. 在Main函数中初始化环境,再使用RUN_ALL_TEST()宏运行测试案例。

优点:

    1. 我们的测试案例本身就是一个exe工程,编译之后可以直接运行,非常的方便。

    2. 编写测试案例变的非常简单(使用一些简单的宏如TEST),让我们将更多精力花在案例的设计和编写上。

    3. 提供了强大丰富的断言的宏,用于对各种不同检查点的检查。

    4. 提高了丰富的命令行参数对案例运行进行一系列的设置。

================================我是华丽的分割线================================

 

二、断言

1、操作符<<

如果不使用<<操作符自定义输出的话: 

[cpp]  view plain copy
  1. for (int i = 0; i < x.size(); ++i)   
  2. {  
  3.     EXPECT_EQ(x[i], y[i]);  
  4. }  


看到的结果将是这样的,你根本不知道出错时 i 等于几:

[cpp]  view plain copy
  1. g:\myproject\c++\gtestdemo\gtestdemo\gtestdemo.cpp(25): error: Value of: y[i]  
  2.   Actual: 4  
  3. Expected: x[i]  
  4. Which is: 3  

如果使用<<操作符将一些重要信息输出的话:

[cpp]  view plain copy
  1. for (int i = 0; i < x.size(); ++i)  
  2. {  
  3.     EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;  
  4. }  
从输出结果中就可以定位到在 i = 2 时出现了错误。这样的输出结果看起来更加有用,容易理解: 
[cpp]  view plain copy
  1. g:\myproject\c++\gtestdemo\gtestdemo\gtestdemo.cpp(25): error: Value of: y[i]  
  2. Actual: 4  
  3. Expected: x[i]  
  4. Which is: 3  
  5. Vectors x and y differ at index 2  
2、  
布尔值检查
Fatal assertion Nonfatal assertion Verifies
ASSERT_TRUE(condition); EXPECT_TRUE(condition); condition is true
ASSERT_FALSE(condition); EXPECT_FALSE(condition); condition is false
3、数值型数据检查
Fatal assertion Nonfatal assertion Verifies
ASSERT_EQ(expectedactual); EXPECT_EQ(expectedactual); expected == actual
ASSERT_NE(val1val2); EXPECT_NE(val1val2); val1 != val2
ASSERT_LT(val1val2); EXPECT_LT(val1val2); val1 < val2
ASSERT_LE(val1val2); EXPECT_LE(val1val2); val1 <= val2
ASSERT_GT(val1val2); EXPECT_GT(val1val2); val1 > val2
ASSERT_GE(val1val2); EXPECT_GE(val1val2); val1 >= val2
4、字符串检查 
Fatal assertion Nonfatal assertion Verifies
ASSERT_STREQ(expected_stractual_str); EXPECT_STREQ(expected_stractual_str); the two C strings have the same content
ASSERT_STRNE(str1str2); EXPECT_STRNE(str1str2); the two C strings have different content
ASSERT_STRCASEEQ(expected_stractual_str); EXPECT_STRCASEEQ(expected_stractual_str); the two C strings have the same content, ignoring case
ASSERT_STRCASENE(str1str2); EXPECT_STRCASENE(str1str2); the two C strings have different content, ignoring case
注意:EXPECT_STRCASEEQ 不支持unicode,其它均支持ansi和unicode。并且字符串unicode时,输出错误提示的时候是乱码,目前不知道怎么解决。
5、显示返回成功或失败

直接返回成功:SUCCEED();

返回失败: 

Fatal assertion Nonfatal assertion
FAIL(); ADD_FAILURE();
注意:FAIL();Fatal Assertion,不往下执行该案例。
     ADD_FAILURE();  // None Fatal Asserton,继续往下执行。   
6、异常检查
Fatal assertion Nonfatal assertion Verifies
ASSERT_THROW(statementexception_type); EXPECT_THROW(statementexception_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
例如:
[cpp]  view plain copy
  1. int Foo(int a, int b)  
  2. {  
  3.     if (a == 0 || b == 0)  
  4.     {  
  5.         throw "don't do that";  
  6.     }  
  7.     int c = a % b;  
  8.     if (c == 0)  
  9.         return b;  
  10.     return Foo(b, c);  
  11. }  
  12.   
  13. TEST(FooTest, HandleZeroInput)  
  14. {  
  15.     EXPECT_ANY_THROW(Foo(10, 0));  
  16.     EXPECT_THROW(Foo(0, 5), char*);  
  17. }  
7、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个参数

[cpp]  view plain copy
  1. bool MutuallyPrime(int m, int n)  
  2. {  
  3.     return Foo(m , n) > 1;  
  4. }  
  5.   
  6. TEST(PredicateAssertionTest, Demo)  
  7. {  
  8.     int m = 5, n = 6;  
  9.     EXPECT_PRED2(MutuallyPrime, m, n);  
  10. }  

当失败时,返回错误信息:

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
... ...

用法示例:

[cpp]  view plain copy
  1. testing::AssertionResult AssertFoo(const char* m_expr, const char* n_expr, const char* k_expr, int m, int n, int k) {  
  2.     if (Foo(m, n) == k)  
  3.         return testing::AssertionSuccess();  
  4.     testing::Message msg;  
  5.     msg << m_expr << " 和 " << n_expr << " 的最大公约数应该是:" << Foo(m, n) << " 而不是:" << k_expr;  
  6.     return testing::AssertionFailure(msg);  
  7. }  
  8.   
  9. TEST(AssertFooTest, HandleFail)  
  10. {  
  11.     EXPECT_PRED_FORMAT3(AssertFoo, 3, 6, 2);  
  12. }  

失败时,输出信息:

error: 3 和 6 的最大公约数应该是:3 而不是:2

8、浮点型检查
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

9、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

10、类型检查

类型检查失败时,直接导致代码编不过,难得用处就在这?看下面的例子:

[cpp]  view plain copy
  1. template <typename T> class FooType {  
  2. public:  
  3.     void Bar() { testing::StaticAssertTypeEq<int, T>(); }  
  4. };  
  5. TEST(TypeAssertionTest, Demo)  
  6. {  
  7.     FooType<bool> fooType;  
  8.     fooType.Bar();  
  9. }  

================================我是华丽的分割线================================

 

三、事件机制

1、前言

gtest提供了多种事件机制,非常方便我们在案例之前或之后做一些操作。总结一下gtest的事件一共有3种:

(1)全局的,所有案例执行前后。

(2)TestSuite级别的,在某一批案例中第一个案例前,最后一个案例执行后。

(3)TestCase级别的,每个TestCase前后。

2、全局事件

要实现全局事件,必须写一个类,继承testing::Environment类,实现里面的SetUp和TearDown方法。

(1). SetUp()方法在所有案例执行前执行

(2). TearDown()方法在所有案例执行后执行

[cpp]  view plain copy
  1. class FooEnvironment : public testing::Environment  
  2. {  
  3.  public:  
  4.   virtual void SetUp()  
  5.   {  
  6.    std::cout << "Foo FooEnvironment SetUP" << std::endl;  
  7.   }  
  8.   virtual void TearDown()  
  9.   {  
  10.    std::cout << "Foo FooEnvironment TearDown" << std::endl;  
  11.   }  
  12. };  


当然,这样还不够,我们还需要告诉gtest添加这个全局事件,我们需要在main函数中通过testing::AddGlobalTestEnvironment方法将事件挂进来,也就是说,我们可以写很多个这样的类,然后将他们的事件都挂上去。

[cpp]  view plain copy
  1. int _tmain(int argc, _TCHAR* argv[])  
  2.  {  
  3.      testing::AddGlobalTestEnvironment(new FooEnvironment);  
  4.      testing::InitGoogleTest(&argc, argv);  
  5.      return RUN_ALL_TESTS();  
  6.  }  


3、TestSuite事件

我们需要写一个类,继承testing::Test,然后实现两个静态方法
(1). SetUpTestCase() 方法在第一个TestCase之前执行
(2). TearDownTestCase() 方法在最后一个TestCase之后执行

[cpp]  view plain copy
  1. class FooTest : public testing::Test {  
  2.     protected:  
  3.         static void SetUpTestCase() {  
  4.             shared_resource_ = new ;  
  5.         }  
  6.         static void TearDownTestCase() {  
  7.             delete shared_resource_;  
  8.             shared_resource_ = NULL;  
  9.         }  
  10.         // Some expensive resource shared by all tests.  
  11.         static T* shared_resource_;  
  12. };  


在编写测试案例时,我们需要使用TEST_F这个宏一个参数必须是上面类的名字,代表一个TestSuite。

[cpp]  view plain copy
  1. TEST_F(FooTest, Test1)  
  2. {  
  3.     // you can refer to shared_resource here   
  4. }  
  5. TEST_F(FooTest, Test2)  
  6. {  
  7.     // you can refer to shared_resource here   
  8. }  


 4、TestCase事件 

TestCase事件是挂在每个案例执行前后的,实现方式和上面的几乎一样,不过需要实现的是SetUp方法和TearDown方法:

(1). SetUp()方法在每个TestCase之前执行

(2). TearDown()方法在每个TestCase之后执行

[cpp]  view plain copy
  1. class FooCalcTest:public testing::Test  
  2. {  
  3.     protected:  
  4.         virtual void SetUp()  
  5.         {  
  6.             m_foo.Init();  
  7.         }  
  8.         virtual void TearDown()  
  9.         {  
  10.             m_foo.Finalize();  
  11.         }  
  12.   
  13.     FooCalc m_foo;  
  14. };  
  15.   
  16. TEST_F(FooCalcTest, HandleNoneZeroInput)  
  17. {  
  18.     EXPECT_EQ(4, m_foo.Calc(12, 16));  
  19. }  
  20.   
  21. TEST_F(FooCalcTest, HandleNoneZeroInput_Error)  
  22. {  
  23.     EXPECT_EQ(5, m_foo.Calc(12, 16));  
  24. }  

注意:用TestSuite和TestCase可以使用成员变量吗,测试前的环境设置等。开始时一直在设置SetUp和TearDown,每次都运行,很纠结,后来才知道应该按照TestSuite方式设置SetUpTestCase就正常了。一定注意使用TEST_F而不是TEST。

================================我是华丽的分割线================================

 

四、参数化

[cpp]  view plain copy
  1. TEST(IsPrimeTest, HandleTrueReturn)  
  2. {  
  3.     EXPECT_TRUE(IsPrime(3));  
  4.     EXPECT_TRUE(IsPrime(5));  
  5.     EXPECT_TRUE(IsPrime(11));  
  6.     EXPECT_TRUE(IsPrime(23));  
  7.     EXPECT_TRUE(IsPrime(17));  
  8. }  

像这样的测试案例,要写很多组数据,冗余。因此我们可以使用参数化:

(1)告诉gtest你的参数类型是什么

你必须添加一个类,继承testing::TestWithParam<T>,其中T就是你需要参数化的参数类型,比如上面的例子,我需要参数化一个int型的参数 

[cpp]  view plain copy
  1. class IsPrimeParamTest : public::testing::TestWithParam<int>  
  2. {  
  3.   
  4. };  

(2)告诉gtest你拿到参数的值后,具体做些什么样的测试

这里,使用一个新的宏TEST_P,在TEST_P宏里,使用GetParam()获取当前的参数的具体值。

[cpp]  view plain copy
  1. TEST_P(IsPrimeParamTest, HandleTrueReturn)  
  2. {  
  3.     int n =  GetParam();  
  4.     EXPECT_TRUE(IsPrime(n));  
  5. }  

(3)告诉gtest你想要测试的参数范围是什么

使用INSTANTIATE_TEST_CASE_P这宏来告诉gtest你要测试的参数范围:

[cpp]  view plain copy
  1. 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)andValuesIn(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

(4)参数化后的测试案例名

 命名规则大概为:prefix/test_case_name.test.name/index 

 2、类型参数化


gtest还提供了应付各种不同类型的数据时的方案,以及参数化类型的方案。

首先定义一个模版类,继承testing::Test:

[cpp]  view plain copy
  1. template <typename T>  
  2. class CFooTest : public testing::Test {  
  3.  public:  
  4.     
  5.   typedef std::list<T> List;  
  6.   static T shared_;  
  7.   T value_;  
  8. };  


接着我们定义需要测试到的具体数据类型,比如下面定义了需要测试char,int和unsigned int :

[cpp]  view plain copy
  1. typedef testing::Types<charint, unsigned int> MyTypes;  
  2. TYPED_TEST_CASE(FooTest, MyTypes);  

 

运用TYPED_TEST宏,在声明模版的数据类型时,使用TypeParam

[cpp]  view plain copy
  1. TYPED_TEST(CFooTest, DoesBlah) {  
  2.   // Inside a test, refer to the special name TypeParam to get the type  
  3.   // parameter.  Since we are inside a derived class template, C++ requires  
  4.   // us to visit the members of FooTest via 'this'.  
  5.   TypeParam n = this->value_;  
  6.   
  7.   // To visit static members of the fixture, add the 'TestFixture::'  
  8.   // prefix.  
  9.   n += TestFixture::shared_;  
  10.   
  11.   // To refer to typedefs in the fixture, add the 'typename TestFixture::'  
  12.   // prefix.  The 'typename' is required to satisfy the compiler.  
  13.   typename TestFixture::List values;  
  14.   values.push_back(n);  
  15.     
  16. }  


上面的例子看上去也像是类型的参数化,但是还不够灵活,因为需要事先知道类型的列表。gtest还提供一种更加灵活的类型参数化的方式,允许你在完成测试的逻辑代码之后再去考虑需要参数化的类型列表,并且还可以重复的使用这个类型列表。下面也是官方的例子:

[cpp]  view plain copy
  1. template <typename T>  
  2. class FooTest : public testing::Test {  
  3.     
  4. };  
  5.   
  6. TYPED_TEST_CASE_P(FooTest);  


使用宏TYPED_TEST_P

[cpp]  view plain copy
  1. TYPED_TEST_P(FooTest, DoesBlah) {  
  2.   // Inside a test, refer to TypeParam to get the type parameter.  
  3.   TypeParam n = 0;  
  4.     
  5. }  
  6.   
  7. TYPED_TEST_P(FooTest, HasPropertyA) {  }  


接着,我们需要我们上面的案例,使用REGISTER_TYPED_TEST_CASE_P宏,

第一个参数是testcase的名称,后面的参数是test的名称

[cpp]  view plain copy
  1. REGISTER_TYPED_TEST_CASE_P(FooTest, DoesBlah, HasPropertyA);  


接着指定需要的类型列表:

[cpp]  view plain copy
  1. typedef testing::Types<charint, unsigned int> MyTypes;  
  2. INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes);  


================================我是华丽的分割线================================

 

五、死亡测试——程序崩溃测试

1、前言

通常在测试过程中,我们需要考虑各种各样的输入,有的输入可能直接导致程序崩溃,这时我们就需要检查程序是否按照预期的方式挂掉,这也就是所谓的“死亡测试”。gtest的死亡测试能做到在一个安全的环境下执行崩溃的测试案例,同时又对崩溃结果进行验证。

2、使用的宏
Fatal assertionNonfatal assertionVerifies
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 matchespredicate

 由于有些异常只在Debug下抛出,因此还提供了*_DEBUG_DEATH,用来处理Debug和Realease下的不同。

3、*_DEATH(statement, regex`)

(1)statement是被测试的代码语句

(2)regex是一个正则表达式,用来匹配异常时在stderr中输出的内容
如下面的例子:

[cpp]  view plain copy
  1. void Foo()  
  2.  {  
  3.      int *pInt = 0;  
  4.      *pInt = 42 ;  
  5.  }  
  6.    
  7. TEST(FooDeathTest, Demo)  
  8.  {  
  9.      EXPECT_DEATH(Foo(), "");  
  10.  }  

重要:编写死亡测试案例时,TEST的第一个参数,即testcase_name,请使用DeathTest后缀。原因是gtest会优先运行死亡测试案例,应该是为线程安全考虑。

4、*_EXIT(statement, predicate, regex`)

(1)statement是被测试的代码语句

(2)predicate 在这里必须是一个委托,接收int型参数,并返回bool。只有当返回值为true时,死亡测试案例才算通过。gtest提供了一些常用的predicate:

[cpp]  view plain copy
  1. testing::ExitedWithCode(exit_code)  

如果程序正常退出并且退出码与exit_code相同则返回 true

[cpp]  view plain copy
  1. testing::KilledBySignal(signal_number)  // Windows下不支持  

如果程序被signal_number信号kill的话就返回true

(3)regex是一个正则表达式,用来匹配异常时在stderr中输出的内容

这里, 要说明的是,*_DEATH其实是对*_EXIT进行的一次包装,*_DEATH的predicate判断进程是否以非0退出码退出或被一个信号杀死

例子:

[cpp]  view plain copy
  1. TEST(ExitDeathTest, Demo)  
  2. {  
  3.     EXPECT_EXIT(_exit(1),  testing::ExitedWithCode(1),  "");  
  4. }  

 

5、*_DEBUG_DEATH

先来看定义:

[cpp]  view plain copy
  1. #ifdef NDEBUG  
  2.   
  3. #define EXPECT_DEBUG_DEATH(statement, regex) \  
  4.   do { statement; } while (false)  
  5.   
  6. #define ASSERT_DEBUG_DEATH(statement, regex) \  
  7.   do { statement; } while (false)  
  8.   
  9. #else  
  10.   
  11. #define EXPECT_DEBUG_DEATH(statement, regex) \  
  12.   EXPECT_DEATH(statement, regex)  
  13.   
  14. #define ASSERT_DEBUG_DEATH(statement, regex) \  
  15.   ASSERT_DEATH(statement, regex)  
  16.   
  17. #endif  // NDEBUG for EXPECT_DEBUG_DEATH  

可以看到,在Debug版和Release版本下, *_DEBUG_DEATH的定义不一样。因为很多异常只会在Debug版本下抛出,而在Realease版本下不会抛出,所以针对Debug和Release分别做了不同的处理。看gtest里自带的例子就明白了:

[cpp]  view plain copy
  1. int DieInDebugElse12(int* sideeffect) {  
  2.     if (sideeffect) *sideeffect = 12;  
  3. #ifndef NDEBUG  
  4.     GTEST_LOG_(FATAL, "debug death inside DieInDebugElse12()");  
  5. #endif  // NDEBUG  
  6.     return 12;  
  7. }  
  8.   
  9. TEST(TestCase, TestDieOr12WorksInDgbAndOpt)  
  10. {  
  11.     int sideeffect = 0;  
  12.     // Only asserts in dbg.  
  13.     EXPECT_DEBUG_DEATH(DieInDebugElse12(&sideeffect), "death");  
  14.   
  15.     #ifdef NDEBUG  
  16.     // opt-mode has sideeffect visible.  
  17.     EXPECT_EQ(12, sideeffect);  
  18.     #else  
  19.     // dbg-mode no visible sideeffect.  
  20.     EXPECT_EQ(0, sideeffect);  
  21.     #endif  
  22. }  

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 characterc
\\dmatches any decimal digit
\\Dmatches any character that's not a decimal digit
\\fmatches \f
\\nmatches \n
\\rmatches \r
\\smatches any ASCII whitespace, including\n
\\Smatches any character that's not a whitespace
\\tmatches \t
\\vmatches \v
\\wmatches any letter, _, or decimal digit
\\Wmatches any character that\\w doesn't match
\\cmatches any literal characterc, which must be a punctuation
.matches any single character except\n
A?matches 0 or 1 occurrences ofA
A*matches 0 or many occurrences ofA
A+matches 1 or many occurrences ofA
^matches the beginning of a string (not that of each line)
$matches the end of a string (not that of each line)
xymatches x followed byy

 gtest定义两个宏,用来表示当前系统支持哪套正则表达式风格:

(1)POSIX风格:GTEST_USES_POSIX_RE = 1

(2)Simple风格:GTEST_USES_SIMPLE_RE=1

7、死亡测试运行方式

1. fast方式(默认的方式)

[cpp]  view plain copy
  1. testing::FLAGS_gtest_death_test_style = "fast";  

2. threadsafe方式

[cpp]  view plain copy
  1. testing::FLAGS_gtest_death_test_style = "threadsafe";  

你可以在 main() 里为所有的死亡测试设置测试形式,也可以为某次测试单独设置。Google Test会在每次测试之前保存这个标记并在测试完成后恢复,所以你不需要去管这部分工作 。如:

[cpp]  view plain copy
  1. TEST(MyDeathTest, TestOne) {  
  2.   testing::FLAGS_gtest_death_test_style = "threadsafe";  
  3.   // This test is run in the "threadsafe" style:  
  4.   ASSERT_DEATH(ThisShouldDie(), "");  
  5. }  
  6.   
  7. TEST(MyDeathTest, TestTwo) {  
  8.   // This test is run in the "fast" style:  
  9.   ASSERT_DEATH(ThisShouldDie(), "");  
  10. }  
  11.   
  12. int main(int argc, char** argv) {  
  13.   testing::InitGoogleTest(&argc, argv);  
  14.   testing::FLAGS_gtest_death_test_style = "fast";  
  15.   return RUN_ALL_TESTS();  
  16. }  
8、注意事项

(1)不要在死亡测试里释放内存。

(2)在父进程里再次释放内存。

(3)不要在程序中使用内存堆检查。

9、总结

关于死亡测试,gtest官方的文档已经很详细了,同时在源码中也有大量的示例。如想了解更多的请参考官方的文档,或是直接看gtest源码。

简单来说,通过*_DEATH(statement, regex`)和*_EXIT(statement, predicate, regex`),我们可以非常方便的编写导致崩溃的测试案例,并且在不影响其他案例执行的情况下,对崩溃案例的结果进行检查。

================================我是华丽的分割线================================

 六、运行参数

1、基本介绍

前面提到,对于运行参数,gtest提供了三种设置的途径:

(1)系统环境变量

(2)命令行参数

(3)代码中指定FLAG

因为提供了三种途径,就会有优先级的问题, 有一个原则是,最后设置的那个会生效。不过总结一下,通常情况下,比较理想的优先级为:

命令行参数 > 代码中指定FLAG > 系统环境变量

为什么我们编写的测试案例能够处理这些命令行参数呢?是因为我们在main函数中,将命令行参数交给了gtest,由gtest来搞定命令行参数的问题。

[cpp]  view plain copy
  1. int _tmain(int argc, _TCHAR* argv[])  
  2.  {  
  3.      testing::InitGoogleTest(&argc, argv);  
  4.      return RUN_ALL_TESTS();  
  5.  }  

如果需要在代码中指定FLAG,可以使用testing::GTEST_FLAG这个宏来设置。比如相对于命令行参数--gtest_output,可以使用testing::GTEST_FLAG(output) = "xml:";来设置。注意到了,不需要加--gtest前缀了。同时,推荐将这句放置InitGoogleTest之前,这样就可以使得对于同样的参数,命令行参数优先级高于代码中指定

[cpp]  view plain copy
  1. int _tmain(int argc, _TCHAR* argv[])  
  2.  {  
  3.      testing::GTEST_FLAG(output) = "xml:";  
  4.      testing::InitGoogleTest(&argc, argv);  
  5.      return RUN_ALL_TESTS();  
  6.  }  

最后再来说下第一种设置方式-系统环境变量。如果需要gtest的设置系统环境变量,必须注意的是:

(1)系统环境变量全大写,比如对于--gtest_output,响应的系统环境变量为:GTEST_OUTPUT

(2)有一个命令行参数例外,那就是--gtest_list_tests,它是不接受系统环境变量的。(只是用来罗列测试案例名称)

2、参数列表

如果想要获得详细的命令行说明,直接运行你的案例,输入命令行参数:/? 或 --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前缀,比如:

[cpp]  view plain copy
  1. // Tests that Foo does Abc.  
  2. TEST(FooTest, DISABLED_DoesAbc) {  }  
  3.   
  4. class DISABLED_BarTest : public testing::Test {  };  
  5.   
  6. // Tests that Bar does Xyz.  
  7. 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的案例。

(2) 测试案例输出

命令行参数说明
--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的输出内容后面介绍吧。 

 3. 对案例的异常处理

命令行参数说明
--gtest_break_on_failure调试模式下,当案例失败时停止,方便调试
--gtest_throw_on_failure当案例失败时以C++异常的方式抛出
--gtest_catch_exceptions

是否捕捉异常。gtest默认是不捕捉异常的,因此假如你的测试案例抛了一个异常,很可能会弹出一个对话框,这非常的不友好,同时也阻碍了测试案例的运行。如果想不弹这个框,可以通过设置这个参数来实现。如将--gtest_catch_exceptions设置为一个非零的数。

注意:这个参数只在Windows下有效。

 

3、XML报告输出格式

[html]  view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <testsuites tests="3" failures="1" errors="0" time="35" name="AllTests">  
  3.   <testsuite name="MathTest" tests="2" failures="1"errors="0" time="15">  
  4.     <testcase name="Addition" status="run" time="7" classname="">  
  5.       <failure message="Value of: add(1, 1)  Actual: 3 Expected: 2" type=""/>  
  6.       <failure message="Value of: add(1, -1)  Actual: 1 Expected: 0" type=""/>  
  7.     </testcase>  
  8.     <testcase name="Subtraction" status="run" time="5" classname="">  
  9.     </testcase>  
  10.   </testsuite>  
  11.   <testsuite name="LogicTest" tests="1" failures="0" errors="0" time="5">  
  12.     <testcase name="NonContradiction" status="run" time="5" classname="">  
  13.     </testcase>  
  14.   </testsuite>  
  15. </testsuites>  

从报告里可以看出,我们之前在TEST等宏中定义的测试案例名称(testcase_name)在xml测试报告中其实是一个testsuite name,而宏中的测试名称(test_name)在xml测试报告中是一个testcase name,概念上似乎有点混淆,就看你怎么看吧。

当检查点通过时,不会输出任何检查点的信息。当检查点失败时,会有详细的失败信息输出来failure节点。

在我使用过程中发现一个问题,当我同时设置了--gtest_filter参数时,输出的xml报告中还是会包含所有测试案例的信息,只不过那些不被执行的测试案例的status值为“notrun”。而我之前认为的输出的xml报告应该只包含我需要运行的测试案例的信息。不知是否可提供一个只输出需要执行的测试案例的xml报告。因为当我需要在1000个案例中执行其中1个案例时,在报告中很难找到我运行的那个案例,虽然可以查找,但还是很麻烦。
================================我是华丽的分割线================================

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值