C++Google C++单元测试框架—Gtest框架简介(译文)

同一、设置一个初的测试项目

  于于是google
test写测试项目之前,需要先编译gtest到library库并以测试和那链接。我们也一些盛的构建系统提供了构建文件: msvc/ for
Visual Studio, xcode/ for Mac Xcode, make/ for GNU
make, codegear/ for Borland C++ Builder.

比方你的构建系统未以是榜及,在googletest根目录有autotools的脚本(不推荐使用)和CMakeLists.txtCMake(推荐)。你得望make
/ Makefile来打探什么编译Google
Test(基本上你想以头文件中行使GTEST_ROOT和GTEST_ROOT /
include来编译src /
gtest-all.cc路径,其中GTEST_ROOT是Google测试根目录)。

  一旦你会编译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上添加依赖)。

次、基本概念

当用谷歌测试,您首先要写断言,断言是反省规范是否为真正话。一个预言的结果好是打响,非致命性失败,或致命的败。如果一个沉重失败出现,它见面停下时之函数;否则程序继续健康运作。

测试用断言验证代码的行为。如果一个测试崩溃或者出一个破产的预言,那么失败;否则成功。

一个测试用例包含一个要么多个测试。 您当以测试分组为体现测试代码结构的测试用例。当测试用例中之大多独测试需要共享公共的靶子与子程序时,你可把它放上一个test
fixture 
class(测试夹具类)。

一个测试程序可以涵盖多个测试用例。

而今我们以说明如何编写测试程序,从单个断言级别开始,并构建测试与测试用例。

三、断言

    Google
Test断言是看似于函数调用的大幅度。您可由此对该一言一行展开预言来测试类或函数。当断言失败时,Google
Test会打印断言的源文件和行号位置及失败消息。您还好供于定义失败消息,该消息将叠加到Google测试的音遭受。

预言是成对的,测试与同码事,但针对当前函数有例外之影响。
ASSERT_ *本子在砸时会变卦致命错误,并暂停当前函数。 EXPECT_
*
本生成非致命性故障,不会见暂停当前函数。通常事先使用EXPECT_
*,因为它允许在测试着告知多只故障。但是,如果失败时函数继续运行无意思,则应采取ASSERT_
*。

为挫败的ASSERT_
*立即从当下函数返回,可能过了那个后底清理代码,它可能引致资源泄漏。根据泄漏的特性,它或许值得修复也说不定无值得修复–所以把这记在中心,如果您生出一个堆积检测错误需要小心是啊招的。

而提供从定义失败消息,只需要采用<<运算符或平等多重此类运算符将该流式传输到宏中即可。一个事例:

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;
}

  示例:

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

 C++ 1

   任何可以流式传输至ostream的事物都得流式传输到断言宏,特别是C字符串和字符串对象。
如果一个宽字符串(Windows上的wchar_t *,TCHAR
*以UNICODE模式下,或者std ::
wstring)被流化到一个预言,当打印时她以给移为UTF-8。

季、基本断言

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

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_
*发非致命失败,允许函数继续运行。
在管一情景下,断言失败意味着其蕴含的测试失败。

五、二进制比较

本节介绍于单薄独价的预言。

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为同等的不二法门处理这点儿个参数。

六、字符串比较

该组中之预言比较单薄独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.)。

七、简单测试

创建测试:

 
1.运用TEST()宏来定义及命名测试函数,这些是无回去回值的一般性C++函数。
 
2.以斯函数中,连同要包括的别样有效的C++语句子,使用各种Google
Test断言来检查值。
  3.测试的结果由于断言确定;
如果测试着之任何断言失败(致命或非致命),或者一旦测试崩溃,则      
 整个测试失败。 否则,它成功。

TEST(test_case_name, test_name) {
 ... test body ...
}

TEST()参数从一般到一定。
第一个参数是测试用例的称号,第二单参数是测试用例中之测试名称。
这简单独名必须是行得通之C ++标识符,并且它们不答应包含下划线(_)。
测试的真名由该含有的测试用例及其个人称号组成。来自不同测试用例的测试好享相同之个体名。

比如说,让咱用一个简单易行的平头函数:

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

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

// 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。

八、测试夹具(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(),因为它同意而看测试夹具中之靶子与子程序:

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队列类编写测试,它起以下接口:

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是为测试的近乎。

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()和这个夹具编写测试。

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测试。

九、调用测试

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测试功能(例如线程安全死亡测试)冲突,因此无吃支持。

 十、写Main函数

卿得自夫法开始:

#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库。

十 一、Visual C ++用户之要说明

如若您把你的测试放入一个仓库,你的main()函数在不同的库房或者当您的
.exe文件中,这些测试用不见面运行。 原因是Visual C ++中之一个荒谬。
当您定义测试时,Google测试会创有静态对象来报它们。
这些目标没起另地方引用,但她的构造函数仍然当运行。 当Visual C
++链接器发现仓库中没自其他地方引用时,它见面丢掉来该库。
您得通过主程序中之测试来引用您的库房,以戒链接器丢弃它。
这里是什么样做到的。 在你的库代码中宣称一个函数:

。。。。。省略啦,如果用VC再来看。

十二、从此间到何去

恭喜! 您就上学了Google测试基础知识。
您可起来修和运作Google
Test测试,阅读有演示,或连续读书AdvancedGuide,其中介绍了重复多立竿见影之Google测试功能。

 

十三、已知道的限量

Google测试旨在线程安全。
在pthreads库可用之网上,实现是线程安全的。
目前,在另系统(例如Windows)上还要采用有限独线程的Google
Test断言是未安全之。
在多数测试中,这不是一个题材,因为一般断言是当主线程遭遇就的。
如果您想拉,你可自愿也你的阳台在gtest-port.h中贯彻必要之协同原语。