Google Test使用

转载:Google Test使用简介_googletest是用来干什么的? c_努力学习的小刘123的博客-CSDN博客

如果想要使用Google Test,首先要学会编写断言。

1. 什么是断言
断言就是那些用来检查一个条件是否为true的语句。一个断言可以有三种结果,分别是成功(Success)、非致命失败(nonfatal failure)和致命失败(fatal failure)。当一个致命失败发生时,它将终止对应的测试用例的执行;不是致命失败,则程序会继续执行下去。

2. 不同断言造成的结果
对于一个测试点,我们有两类断言可以对它进行测试。不同的断言会对不同的用例有不同的影响。

ASSER_*这种断言会在断言失败时产生致命失败,并且终止当前测试用例。(由于这种断言在失败会立即返回,所以可能会跳过后边代码中清理(clean-up)代码,这可能会导致内存泄露)

EXPECT_*这种断言在失败时会造成非致命失败,这将不会终止当前测试用例。(这种断言出错后不会终止当前测试用例,而是继续执行。若后续的断言语句依然有不通过的,则后续的断言依然可以报出测试失败信息)

如果需要自定义失败消息,可以采用如下方式:
 

ASSERT_EQ(x.size(), y.size()) << "自定义的消息";
for (int i = 0; i < x.size(); ++i) {
  EXPECT_EQ(x[i], y[i]) << "自定义的消息" << i;
}

说白了就是使用<<运算符或多个该运算符序列将自定义的消息流式传输到宏中。

任何可以流式传入ostream的都可以流式传入断言宏。尤其是C字符串和string对象。如果一个宽字符串(wchar_t*,TCHAR*在Windowss上的UNICODE模式,或模式std::wstring)流式传输到一个断言,在打印时将被转换为UTF-8格式)。

3. 断言的类型
断言分为三种分别为:基本断言、二元比较断言、字符串比较断言。

(1)基本断言
基本断言也就是最基本的逻辑运算

致命断言非致命断言什么条件下通过
ASSERT_TRUE(事件)EXPECT_TRUE(事件)事件为真
ASSERT_FALSE(事件)EXPECT_FALSE(事件)事件为假

注意点:

  • ASSERT开头的断言失败后产生的是致命失败,而EXPECT开头的断言失败后产生的是非致命失败。
(2)二元比较断言

二元比较断言主要用于两个值之间的比较

致命断言非致命断言什么条件下通过
ASSERT_EQ(value1,value2)EXPECT_EQ(value1,value2)value1==value2
ASSERT_NE(value1,value2)EXPECT_NE(value1,value2)value1!=value2
ASSERT_LT(value1,value2)EXPECT_LT(value1,value2)value1<value2
ASSERT_LE(value1,value2)EXPECT_LE(value1,value2)value1<=value2
ASSERT_GT(value1,value2)EXPECT_GT(value1,value2)value1>value2
ASSERT_GE(value1,value2)EXPECT_GE(value1,value2)value1>=value2

注意点:

  • value1和value2之间必须可以用于比较,不然会报错。
(3)字符串比较断言

该断言主要用于C字符串之间的比较,string对象可以使用EXPECT_EQ,EXPECT_NE等进行比较。

致命断言非致命断言什么条件下通过
ASSERT_STREQ(str1,str2)EXPECT_STREQ(str1,str2)字符串内容相同
ASSERT_STRNE(str1,str2)EXPECT_STRNE(str1,str2)字符串内容不同
ASSERT_STRCASEEQ(str1,str2)EXPECT_STRCASEEQ(str1,str2)字符串内容相同(忽略大小写)
ASSERT_STRCASENE(str1,str2)EXPECT_STRCASENE(str1,str2)字符串内容不同(忽略大小写)

注意点:

断言中的"CASE"表示忽略大小写。NULL空指针和空字符串被认为是不同的。
STREQ和STRNE也能传入宽C字符串(wchar_t*)。如果比较两个宽字符串的断言失败,字符串将会以UTF-8窄字符串的形式打印。
代码实操
下面通过代码的方式进行实践。

基于CMake的实例可以参考这篇博客

1. 基础测试用例
创建测试用例的步骤
使用TEST()定义一个测试用例。
在TEST()函数体中,添加合法有效的C++语句,并使用Google Test的断言去进行检验。
测试的结果是由断言决定的。该测试用例中的任一断言失败(无论是致命断言还是非致命断言),或者测试用例发生了错误,该测试用例就被认定为失败的。否侧,断言成功。
 

TEST(TestSuiteName, TestName) {
   /*在这里书写代码*/
}

TEST()的参数:

第一个参数是测试集的名称,第二个参数是测试用例的名称,该测试用例属于第一个参数所指定的测试集。

这两个名称都必须是合法的C++标识符,并且不应该包含下划线(_)。

一个测试用例的完整名称由包含它的测试集和它自己的名称组成。不同测试集里的

int Factorial(int n);    //仅作为一个例子,假设该函数能够计算n的阶乘

// 测试0的阶乘
TEST(FactorialTest, HandlesZeroInput) {
  EXPECT_EQ(Factorial(0), 1); //如果出错,这种断言会产生非致命失败
}

// 测试正数的阶乘
TEST(FactorialTest, HandlesPositiveInput) {
  EXPECT_EQ(Factorial(1), 1);
  EXPECT_EQ(Factorial(2), 2);
  EXPECT_EQ(Factorial(3), 6);
  EXPECT_EQ(Factorial(8), 40320);
}

测试用例可以使用同样的名称。

现在声明一个简单的函数,并给出其测试集

注意点:

Google Test通过测试集来组织这些测试的结果,所以逻辑相关的测试用例应该被放在相同的测试集中(也就是说,逻辑相关的测试用例,TEST()的第一个参数应该是一样的)

在命名测试集和测试用例时,应该遵循和命名函数和类相同的规范。

2. Test Fixtures
在实际测试的过程中,如果写了多个测试用例,且他们都需要用到相似的数据,可以使用Test Fixtures。

Test Fixtures允许给不同的测试用例重用一些相同的配置。

创建Fixtures的步骤
从::testing::Test派生一个类。类体以protected:开始,因为我们想从子类中访问fixture的成员。
在这个类中,声明一些预计要使用的对象。
如果需要的话,编写一个默认的构造函数或SetUp()函数来为每个测试用例初始化fixture中的对象(注意是SetUp()不是Setup())。
如果需要的话,编写一个析构函数或者TearDown()函数去释放你在SetUp()中分配的资源。详细的使用方法请参考这里。
如果需要的话,定义一些需要共享的子程序。
使用Fixtures时,使用TEST_F()替代TEST(),因为TEST_F()可以使你访问到fixture中的对象和子程序。
 

TEST_F(TestFixtureName, TestName) {
  /*在这里书写代码*/
}

注意点:

同TEST()一样,TEST_F()的第一个参数是测试集名称,但TEST_F()要求第一个参数必须是fixture类的类名。
C++的宏规则不允许开发者仅创建一个宏就可以操纵不同的测试,使用错误的宏将会导致编译错误。因此才没有同一使用TEST(),而是引入了TEST_F()。
在TEST_F()中使用fixture之前必须要定义好fixture类,不然会报错(virtual outside class declaration)。
Google Test会在运行时,为每一个使用TEST_F()定义的测试用例创建一个新的fixture对象,立即调用SetUp()进行初始化,然后运行测试用例,之后会通过调用TearDown()来清理资源,最后删除fixture对象。需要注意的是,同一个测试集中不同的测试用例使用的是不同的fixture对象,Google Test总是在为下一个测试用例创建fixture之前,删除当前测试使用后的fixture。Google Test不会给多个测试用例重用同一个fixture对象。一个测试用例对fixture的任何操作不会影响到其他测试用例。

下面做一个实例
 

/*使用fixture测试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;
  ...
};

首先,定义一个fixture类。一般的,你应该给一个待测类名为Foo的类创建一个名为FooTest的fixture类。

class QueueTest : public ::testing::Test { //从::testing::Test派生一个类 QueueTest
 protected:
  void SetUp() override {
     q1_.Enqueue(1);
     q2_.Enqueue(2);
     q2_.Enqueue(3);
  }
  // void TearDown() override {}
  Queue<int> q0_;
  Queue<int> q1_;
  Queue<int> q2_;
};

类声明完毕后,测试用例如下:

TEST_F(QueueTest, IsEmptyInitially) {
  EXPECT_EQ(q0_.size(), 0);
}

TEST_F(QueueTest, DequeueWorks) {
  int* n = q0_.Dequeue();
  EXPECT_EQ(n, nullptr);

  n = q1_.Dequeue();
  ASSERT_NE(n, nullptr);  //使用ASSERT的原因在于下面的代码需要对指针n解引用,这将会在n是NULL的时候导致段错误
  EXPECT_EQ(*n, 1);
  EXPECT_EQ(q1_.size(), 0);
  delete n;

  n = q2_.Dequeue();
  ASSERT_NE(n, nullptr);
  EXPECT_EQ(*n, 2);
  EXPECT_EQ(q2_.size(), 1);
  delete n;
}

当这些测试用例在运行时:

Google Test构造一个QueueTest对象(如我们可以叫它t1)。
t1.SetUp()初始化t1。
第一个测试用例(IsEmptyInitially)基于t1运行。
t1.TearDown()在这个测试用例结束后被调用。
t1被析构。
上述步骤在一个新的QueueTest对象上重复,这次基于新的QueueTest对象(可以认为这个对象是t2)运行第二个测试用例DequeueWorks。
3. 调用测试用例
TEST()和TEST_F()隐式地把它们的测试注册给 googletest,所以,不像许多其他C++测试框架,你不必为了运行它们而一一列举它们。

在你定义完测试用例之后,你可以使用RUN_ALL_TESTS()运行它们,当所有测试成功时返回0,否则返回1。注意RUN_ALL_TESTS()运行你链接的所有测试——它们可以来自不同的测试集,甚至是不同的源文件。

当RUN_ALL_TESTS()被调用时:

保存所有googletest flags的状态。
为第一个测试用例创建fixture对象
通过SetUp()初始化fixture对象。
基于这个fixture对象运行第一个测试用例。
通过调用TearDown清理fixture对象。
删除fixture。
恢复所有googletest flags的状态。
重复以上所有步骤,知道所有测试运行完毕。
当在执行某一步时发生了致命错误,下面的所有步骤都会被跳过

4. main()函数的编写
大多数用户不需要编写main函数,只要链接gtest_main库就可以,该库提供了合适的入口点。当你想在测试用例执行之前执行一些自定义的操作,但这些操作又不能通过现有的框架去添加(如fixture),就需要编写main函数了。

你编写的main函数需要返回RUN_ALL_TESTS()的返回值。
 

#include "this/package/foo.h"
#include "gtest/gtest.h"

namespace my {
namespace project {
namespace {

// The fixture for testing class Foo.
class FooTest : public ::testing::Test {
 protected:
  // You can remove any or all of the following functions if their bodies would
  // be empty.

  FooTest() {
     // You can do set-up work for each test here.
  }

  ~FooTest() override {
     // You can do clean-up work that doesn't throw exceptions here.
  }

  // If the constr and destructor are not enough for setting up
  // and cleaning up each test, you can define the following methods:
  
  void SetUp() override {
     // Code here will be called immediately after the constructor (right
     // before each test).
  }

  void TearDown() override {
     // Code here will be called immediately after each test (right
     // before the destructor).
  }

  // Class members declared here can be used by all tests in the test suite
  // for Foo.
};
    
// Tests that the Foo::Bar() method does Abc.
TEST_F(FooTest, MethodBarDoesAbc) {
  const std::string input_filepath = "this/package/testdata/myinputfile.dat";
  const std::string output_filepath = "this/package/testdata/myoutputfile.dat";
  Foo f;
  EXPECT_EQ(f.Bar(input_filepath, output_filepath), 0);
}

// Tests that Foo does Xyz.
TEST_F(FooTest, DoesXyz) {
  // Exercises the Xyz feature of Foo.
}

}  // namespace
}  // namespace project
}  // namespace my

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

::testing::InitGoogleTest()函数解析 googletest 标记的命令行参数,并移除所有已识别的标记。这允许用户通过不同的标记控制测试程序的行为。但必须在调用RUN_ALL_TESTS()之前调用这个函数,否则标记将无法得到适当的初始化。AdvancedGuide(高级教程)中有相关的描述。

在Windows下,InitGoogleTest()同样也可以基于宽字符串使用,因此它也可以被用于以UNICODE模式编译的程序。

单纯的编写一个main函数过于麻烦。这也是Google Test 提供了一个基础的main函数实现的原因。如果它能够满足你的需求的话,仅需要将你的测试用例和库gtest_main链接就可以了。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值