热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

GoogleC++单元测试框架Gtest框架简介(译文)

一、设置一个新的测试项目在用googletest写测试项目之前,需要先编译gtest到library库并将测试与其链接。我们为一些流行的构建系统提供了构建文件: msvc forV

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

  在用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 

  示例:

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

 技术分享

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

四、基本断言

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

Fatal assertionNonfatal assertionVerifies
ASSERT_TRUE(condition); EXPECT_TRUE(condition); condition is true
ASSERT_FALSE(condition); EXPECT_FALSE(condition); condition is false

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

五、二进制比较

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

Fatal assertionNonfatal assertionVerifies
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 assertionNonfatal assertionVerifies
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  // 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 q0_;
  Queue q1_;
  Queue 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中实现必要的同步原语。

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


推荐阅读
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了指针的概念以及在函数调用时使用指针作为参数的情况。指针存放的是变量的地址,通过指针可以修改指针所指的变量的值。然而,如果想要修改指针的指向,就需要使用指针的引用。文章还通过一个简单的示例代码解释了指针的引用的使用方法,并思考了在修改指针的指向后,取指针的输出结果。 ... [详细]
  • 在project.properties添加#Projecttarget.targetandroid-19android.library.reference.1..Sliding ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 本文介绍了一种解析GRE报文长度的方法,通过分析GRE报文头中的标志位来计算报文长度。具体实现步骤包括获取GRE报文头指针、提取标志位、计算报文长度等。该方法可以帮助用户准确地获取GRE报文的长度信息。 ... [详细]
  • PDF内容编辑的两种小方法,你知道怎么操作吗?
    本文介绍了两种PDF内容编辑的方法:迅捷PDF编辑器和Adobe Acrobat DC。使用迅捷PDF编辑器,用户可以通过选择需要更改的文字内容并设置字体形式、大小和颜色来编辑PDF文件。而使用Adobe Acrobat DC,则可以通过在软件中点击编辑来编辑PDF文件。PDF文件的编辑可以帮助办公人员进行文件内容的修改和定制。 ... [详细]
  • CentOS 6.5安装VMware Tools及共享文件夹显示问题解决方法
    本文介绍了在CentOS 6.5上安装VMware Tools及解决共享文件夹显示问题的方法。包括清空CD/DVD使用的ISO镜像文件、创建挂载目录、改变光驱设备的读写权限等步骤。最后给出了拷贝解压VMware Tools的操作。 ... [详细]
author-avatar
香港君媚儿上官燕_803
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有