Google C++ 单元测试 GTest

from : http://www.cnblogs.com/jycboy/p/6057677.htmlhtml

1、设置一个新的测试项目

  在用google test写测试项目以前,须要先编译gtest到library库并将测试与其连接。咱们为一些流行的构建系统提供了构建文件: msvc/ for Visual Studio, xcode/ for Mac Xcode, make/ for GNU make, codegear/ for Borland C++ Builder.java

若是你的构建系统不在这个名单上,在googletest根目录有autotools的脚本(不推荐使用)和CMakeLists.txtCMake(推荐)。你能够看看make / Makefile来了解如何编译Google Test(基本上你想在头文件中使用GTEST_ROOT和GTEST_ROOT / include来编译src / gtest-all.cc路径,其中GTEST_ROOT是Google测试根目录)。git

  一旦你可以编译google test库,您应该为您的测试程序建立一个项目或构建目标。Make sure you have GTEST_ROOT/include in the header search path so that the compiler can find "gtest/gtest.h" when compiling your test.把google test库加到你的测试项目中(好比:在VS中在gtest.vcproj上添加依赖)。github

2、基本概念

当使用谷歌测试,您首先要写断言,断言是检查条件是否为真的语句。一个断言的结果能够是成功,非致命性失败,或致命的失败。若是一个致命失败出现,它会终止当前的函数;不然程序继续正常运行。正则表达式

测试使用断言验证代码的行为。若是一个测试崩溃或者有一个失败的断言,那么失败;不然成功。xcode

一个测试用例包含一个或多个测试。 您应该将测试分组为反映测试代码结构的测试用例。当测试用例中的多个测试须要共享公共的对象和子程序时,你能够把它们放进一个test fixture class(测试夹具类)。安全

一个测试程序能够包含多个测试用例。框架

如今咱们将解释如何编写测试程序,从单个断言级别开始,并构建测试和测试用例。ide

3、断言

    Google Test断言是相似于函数调用的宏。您能够经过对其行为进行断言来测试类或函数。当断言失败时,Google Test会打印断言的源文件和行号位置以及失败消息。您还能够提供自定义失败消息,该消息将附加到Google测试的信息中。函数

断言是成对的,测试同一件事,但对当前函数有不一样的影响。 ASSERT_ *版本在失败时会生成致命错误,并停止当前函数。 EXPECT_ *版本生成非致命性故障,不会停止当前函数。一般优先使用EXPECT_ *,由于它们容许在测试中报告多个故障。可是,若是失败时函数继续运行没有意义,则应使用ASSERT_ *。

由于失败的ASSERT_ *当即从当前函数返回,可能跳过其后的清理代码,它可能致使资源泄漏。根据泄漏的性质,它可能值得修复也可能不值得修复--因此把这个记在内心,若是你有一个堆检测错误须要注意是什么致使的。

要提供自定义失败消息,只需使用<<运算符或一系列此类运算符将其流式传输到宏中便可。一个例子:

1
2
3
4
5
ASSERT_EQ(x.size(), y.size()) <<  "Vectors x and y are of unequal length" ;
 
for  ( int  i =  0 ; i < x.size(); ++i) {
   EXPECT_EQ(x[i], y[i]) <<  "Vectors x and y differ at index "  << i;
}

  示例:

1
EXPECT_EQ( 0 , strcmp(s.c_string(), kHelloString2)) <<  "s.c_string:"  << s.c_string() <<  " kHelloString:"  << +kHelloString ;  

 

   任何能够流式传输到ostream的东西均可以流式传输到断言宏,特别是C字符串和字符串对象。 若是一个宽字符串(Windows上的wchar_t *,TCHAR *在UNICODE模式下,或者std :: wstring)被流化到一个断言,当打印时它将被转换为UTF-8。

4、基本断言

这些断言作基本的真/假条件测试。

Fatal assertion Nonfatal assertion Verifies
ASSERT_TRUE(condition); EXPECT_TRUE(condition); condition is true
ASSERT_FALSE(condition); EXPECT_FALSE(condition); condition is false

记住,当它们失败时,ASSERT_ *产生致命失败并从当前函数返回,而EXPECT_ *产生非致命失败,容许函数继续运行。 在任一状况下,断言失败意味着其包含的测试失败。

5、二进制比较

本节介绍比较两个值的断言。

Fatal assertion Nonfatal assertion Verifies
ASSERT_EQ(val1,val2); EXPECT_EQ(val1,val2); val1 == val2
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

在发生故障时,Google测试同时打印val1和val2。

值参数经过断言的比较运算符必须能够比较,不然会出现编译错误。咱们曾经要求参数支持<<运算符,用于流传输到ostream,但从v1.6.0它再也不须要(若是支持<<,则会在断言失败时调用它来打印参数;不然Google Test将尝试以最佳方式打印它们。有关更多详细信息和如何自定义参数的打印,请参阅此Google Mock recipe.。

这些断言可使用用户定义的类型,可是只有当你定义了相应的比较运算符(例如==,<,etc)。若是定义了相应的操做符,则更喜欢使用ASSERT _ *()宏,由于它们不只会打印比较结果,并且还会打印出两个操做数。

参数老是只计算一次。所以,参数有反作用不要紧。然而,与任何普通的C / C ++函数同样,参数的求值顺序是未定义的(即编译器能够自由选择任何顺序),你的代码不该该依赖于任何特定的参数求值顺序。

ASSERT_EQ()指针的指针相等。若是在两个C字符串上使用,它会测试它们是否在同一个内存位置,而不是它们具备相同的值。所以,若是你想比较C字符串(例如const char *)的值,使用ASSERT_STREQ(),稍后将会描述。特别地,要断言C字符串为NULL,请使用ASSERT_STREQ(NULL,c_string)。可是,要比较两个字符串对象,应该使用ASSERT_EQ。

本节中的宏适用于窄和宽字符串对象(string和wstring)。

历史记录:2016年2月以前* _EQ有一个约定,称为ASSERT_EQ(expected,actual),因此不少现有的代码使用这个顺序。 如今* _EQ以相同的方式处理这两个参数。

6、字符串比较

该组中的断言比较两个C字符串的值。 若是要比较两个字符串对象,请改用EXPECT_EQ,EXPECT_NE和etc。

Fatal assertion Nonfatal assertion Verifies
ASSERT_STREQ(str1,str2); EXPECT_STREQ(str1,_str_2); 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(str1,str2); EXPECT_STRCASEEQ(str1,str2); 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

 

注意,断言名中的“CASE”表示忽略大小写。

* STREQ *和* STRNE *也接受宽C字符串(wchar_t *)。 若是两个宽字符串的比较失败,它们的值将打印为UTF-8窄字符串。

NULL指针和空字符串被认为是不一样的。

可用性:Linux,Windows,Mac。

另请参阅:有关更多字符串比较技巧(例如,子字符串,前缀,后缀和正则表达式匹配),请参见高级Google测试指南(Advanced Google Test Guide.)。

7、简单测试

建立测试:

  1.使用TEST()宏来定义和命名测试函数,这些是不返回值的普通C++函数。
  2.在此函数中,连同要包括的任何有效的C++语句,使用各类Google Test断言来检查值。
  3.测试的结果由断言肯定; 若是测试中的任何断言失败(致命或非致命),或者若是测试崩溃,则        整个测试失败。 不然,它成功。

1
2
3
TEST(test_case_name, test_name) {
  ... test body ...
}

TEST()参数从通常到特定。 第一个参数是测试用例的名称,第二个参数是测试用例中的测试名称。 这两个名称必须是有效的C ++标识符,而且它们不该包含下划线(_)。 测试的全名由其包含的测试用例及其我的名称组成。来自不一样测试用例的测试能够具备相同的我的名称。

例如,让咱们使用一个简单的整数函数:

1
int  Factorial( int  n);  // Returns the factorial of n;n!

  此函数的测试用例可能以下所示:

1
2
3
4
5
6
7
8
9
10
11
12
// Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
   EXPECT_EQ( 1 , Factorial( 0 ));
}
 
// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
   EXPECT_EQ( 1 , Factorial( 1 ));
   EXPECT_EQ( 2 , Factorial( 2 ));
   EXPECT_EQ( 6 , Factorial( 3 ));
   EXPECT_EQ( 40320 , Factorial( 8 ));
} 

Google Test经过测试用例对测试结果进行分组,所以逻辑相关的测试应该在同一测试用例中; 换句话说,它们的TEST()的第一个参数应该是相同的。 在上面的例子中,咱们有两个测试,HandlesZeroInput和HandlesPositiveInput,属于同一个测试用例FactorialTest。

8、测试夹具(Test Fixtures):对多个测试使用相同的数据配置

若是你发现本身写了两个或更多的测试来操做相似的数据,你可使用测试夹具。它容许您为几个不一样的测试重复使用相同的对象配置。

要建立夹具,只需:

  1.从:: testing :: Test派生一个类。 使用protected:或public:开始它的主体,由于咱们想从子类     访问fixture成员。
  2.在类中,声明你打算使用的任何对象。
  3.若是须要,能够编写默认构造函数或SetUp()函数来为每一个测试准备对象。 一个常见的错误是     拼写SetUp()为Setup()与一个小u -- 不要让这种状况发生在你身上。
  4.若是须要,写一个析构函数或TearDown()函数来释放你在SetUp()中分配的任何资源。 要     学习何时应该使用构造函数/析构函数,当你应该使用SetUp()/ TearDown()时,请阅读这个 FAQ entry.。
  5.若是须要,定义要分享的测试的子程序。

当使用夹具时,使用TEST_F()而不是TEST(),由于它容许您访问测试夹具中的对象和子程序:

1
2
3
TEST_F(test_case_name, test_name) {
  ... test body ...
}

和TEST()同样,第一个参数是测试用例名,

可是对于TEST_F()第一个参数必须是测试夹具类的名称。 你可能猜到了:_F是夹具。

不幸的是,C ++宏系统不容许咱们建立一个能够处理两种类型的测试的宏。 使用错误的宏会致使编译器错误。

另外,在TEST_F()中使用它以前,你必须首先定义一个测试夹具类,不然将获得编译器错误“virtual outside class declaration”。

对于使用TEST_F()定义的每一个测试,Google Test将:

  1.在运行时建立一个新的测试夹具
  2.当即经过SetUp()初始化,
  3.运行测试
  4.经过调用TearDown()清除
  5.删除测试夹具。 请注意,同一测试用例中的不一样测试具备不一样的测试夹具对象,Google测试始     终会删除测试夹具,而后再建立下一个测试夹具。 Google测试不会为多个测试重复使用相同的       测试夹具。一个测试对夹具的任何更改不会影响其余测试

例如,让咱们为名为Queue的FIFO队列类编写测试,它有如下接口:

1
2
3
4
5
6
7
8
9
template <typename E>  // E is the element type.
class  Queue {
  public :
   Queue();
   void  Enqueue( const  E& element);
   E* Dequeue();  // Returns NULL if the queue is empty.
   size_t size()  const ;
   ...
};

  首先定义一个夹具类。按照惯例,你应该给它名称FooTest,其中Foo是被测试的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class  QueueTest :  public  ::testing::Test {
  protected :
   virtual  void  SetUp() {
     q1_.Enqueue( 1 );
     q2_.Enqueue( 2 );
     q2_.Enqueue( 3 );
   }
 
   // virtual void TearDown() {}
 
   Queue< int > q0_;
   Queue< int > q1_;
   Queue< int > q2_;
};

在这种状况下,不须要TearDown(),由于咱们没必要在每次测试后清理,除了析构函数已经作了什么。

如今咱们将使用TEST_F()和这个夹具编写测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
TEST_F(QueueTest, IsEmptyInitially) {
   EXPECT_EQ( 0 , q0_.size());
}
 
TEST_F(QueueTest, DequeueWorks) {
   int * n = q0_.Dequeue();
   EXPECT_EQ(NULL, n);
 
   n = q1_.Dequeue();
   ASSERT_TRUE(n != NULL);
   EXPECT_EQ( 1 , *n);
   EXPECT_EQ( 0 , q1_.size());
   delete n;
 
   n = q2_.Dequeue();
   ASSERT_TRUE(n != NULL);
   EXPECT_EQ( 2 , *n);
   EXPECT_EQ( 1 , q2_.size());
   delete n;
}

上面使用ASSERT_ *和EXPECT_ *断言。 经验法则( The rule of thumb )是当你但愿测试在断言失败后继续显示更多错误时使用EXPECT_ *,或是在失败后继续使用ASSERT_ *没有意义。 例如,Dequeue测试中的第二个断言是ASSERT_TRUE(n!= NULL),由于咱们须要稍后解引用指针n,这将致使n为NULL时的segfault。

当这些测试运行时,会发生如下状况:

  1.Google Test构造了一个QueueTest对象(咱们称之为t1)。 
  2.t1.SetUp()初始化t1。 
  3.第一个测试(IsEmptyInitially)在t1上运行。 
  4.t1.TearDown()在测试完成后清理。 
  5.t1被析构。 
  6.以上步骤在另外一个QueueTest对象上重复,此次运行DequeueWorks测试。

9、调用测试

TEST()和TEST_F()用Google Test隐式注册他们的测试。 所以,与许多其余C ++测试框架不一样,您没必要从新列出全部定义的测试以运行它们。

定义测试后,可使用RUN_ALL_TESTS()运行它们,若是全部测试成功则返回0,不然返回1。 请注意,RUN_ALL_TESTS()运行连接单元中的全部测试 - 它们能够来自不一样的测试用例,甚至是不一样的源文件。

调用时,RUN_ALL_TESTS()宏:

  1. 保存全部Google测试标记的状态。
  2. 为第一个测试建立测试夹具对象。
  3. 经过SetUp()初始化它。
  4. 在fixture对象上运行测试。
  5. 经过TearDown()清除夹具。
  6. 删除夹具。
  7. 恢复全部Google测试标志的状态。
  8. 重复上述步骤进行下一个测试,直到全部测试运行结束。

此外,若是测试夹具的构造函数在步骤2中产生致命故障,则步骤3-5没有意义,所以它们被跳过。 相似地,若是步骤3产生致命故障,则将跳过步骤4。

重要:您不能忽略RUN_ALL_TESTS()的返回值,不然gcc将给您一个编译器错误。 此设计的基本原理是自动测试服务基于其退出代码而不是其stdout / stderr输出来肯定测试是否已经过; 所以您的main()函数必须返回RUN_ALL_TESTS()的值

此外,您应该只调用一次RUN_ALL_TESTS()。 屡次调用它会与一些高级Google测试功能(例如线程安全死亡测试)冲突,所以不受支持。

 10、写Main函数

你能够从这个样板开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include  "this/package/foo.h"
#include  "gtest/gtest.h"
 
namespace {
// The fixture for testing class Foo.
class  FooTest :  public  ::testing::Test {
  protected :
   // You can remove any or all of the following functions if its body
   // is empty.
 
   FooTest() {
     // You can do set-up work for each test here.
   }
 
   virtual ~FooTest() {
     // You can do clean-up work that doesn't throw exceptions here.
   }
 
   // If the constructor and destructor are not enough for setting up
   // and cleaning up each test, you can define the following methods:
 
   virtual  void  SetUp() {
     // Code here will be called immediately after the constructor (right
     // before each test).
   }
 
   virtual  void  TearDown() {
     // Code here will be called immediately after each test (right
     // before the destructor).
   }
 
   // Objects declared here can be used by all tests in the test case for Foo.
};
 
// Tests that the Foo::Bar() method does Abc.
TEST_F(FooTest, MethodBarDoesAbc) {
   const  string input_filepath =  "this/package/testdata/myinputfile.dat" ;
   const  string output_filepath =  "this/package/testdata/myoutputfile.dat" ;
   Foo f;
   EXPECT_EQ( 0 , f.Bar(input_filepath, output_filepath));
}
 
// Tests that Foo does Xyz.
TEST_F(FooTest, DoesXyz) {
   // Exercises the Xyz feature of Foo.
}
 
}   // namespace
 
int  main( int  argc,  char  **argv) {
   ::testing::InitGoogleTest(&argc, argv);
   return  RUN_ALL_TESTS();
}

:: testing :: InitGoogleTest()函数解析Google测试标志的命令行,并删除全部已识别的标志。 这容许用户经过各类标志控制测试程序的行为,咱们将在AdvancedGuide中介绍。 在调用RUN_ALL_TESTS()以前必须调用此函数,不然标志将没法正确初始化。

在Windows上,InitGoogleTest()也适用于宽字符串,所以它也能够在以UNICODE模式编译的程序中使用。

但也许你认为编写全部这些main()函数是太多的工做? 咱们彻底赞成你的见解,这就是为何Google Test提供了main()的基本实现。 若是它适合你的须要,而后只是连接你的测试与gtest_main库。

十 1、Visual C ++用户的重要说明

若是你把你的测试放入一个库,你的main()函数在不一样的库或在你的 .exe文件中,这些测试将不会运行。 缘由是Visual C ++中的一个错误。 当您定义测试时,Google测试会建立一些静态对象来注册它们。 这些对象没有从其余地方引用,但它们的构造函数仍然应该运行。 当Visual C ++连接器发现库中没有从其余地方引用时,它会抛出该库。 您必须经过主程序中的测试来引用您的库,以防止连接器丢弃它。 这里是如何作到的。 在你的库代码中声明一个函数:

。。。。。省略啦,若是用VC再来看。

12、从这里到哪里去

恭喜! 您已学习了Google测试基础知识。 您能够开始编写和运行Google Test测试,阅读一些示例,或继续阅读AdvancedGuide,其中介绍了更多有用的Google测试功能。

 

十3、已知的限制

Google测试旨在线程安全。 在pthreads库可用的系统上,实现是线程安全的。 目前,在其余系统(例如Windows)上同时使用两个线程的Google Test断言是不安全的。 在大多数测试中,这不是一个问题,由于一般断言是在主线程中完成的。 若是你想帮助,你能够志愿为您的平台在gtest-port.h中实现必要的同步原语。

相关文章
相关标签/搜索