使用Googletest进行相关测试

简介

GoogleTest提供了一组断言为我们的测试代码行为做判断。你可以检查布尔型状态,通过运算符比较值,判断字符串的值,浮点型值的判断等等。甚至可以进行用户自定义的相关比较。更高级的功能请参看Assertions Reference;

编写原则

1.测试用例应该是具有独立性和可重复性的。调试测试用例为成功或者失败是一个痛苦的过程。googltest通过生成不同的对象,来保证每一个用例运行的独立性。当一个测试用例失败时,googltest同样允许它独立运行以进行快速调试;

2.测试用例应该具有良好的组织,并且可以清晰的反映出测试代码结构。googletest使用testsuite组织具有关联性的相关用例,并且使同属一个test suite的用例可以共享数据和子进程。这种常规的组织方式便于用例的查阅理解同时可以使测试用例易于维护。这种特性对于经常需要切换基线测试代码的人员非常方便。

3.测试用例应该具有稳定性和重用性。谷歌有很多跨平台的代码;所以测试用例也应该可以支持跨平台特性。googletest可以在不同平台工作,使用不同的编译器,只需通过使用不同的配置即可实现。

4.当测试用例失败是, 测试框架应该给出尽可能详尽的信息。googletest不会再第一个测试用例失败时就停止。相反,googletest只会停止当前测试用例,并继续下一条用例的执行。当然,再当前测试用例执行时,你也可以设置googletest报告非致命故障。因此,你可以通过这个特性,再单个的“运行-编辑-编译”周期中,检查和修复多个错误。

5.测试框架应该讲测试用例编写人员从用例流程轮子的书写中解放出来,让测试人员专注于测试内容本身。googletest会自动“注册”所有测试用例,不需要测试开发人员列出所有待测用例即可运行它们。

6.测试用例的开发应该是快速而敏捷的。使用googletest,你可以再不同测试用例之间共享,重用数据,并且可以仅仅定义一次setup(初始化)/teardown(环境恢复),让每一条测试用例均相互独立,互不影响。

如何使用TEST创建测试用例:

​ 1.使用TEST()这个宏来顶一个一个测试用例的名字,这可以创建一个普通的C++函数,这个函数不会有返回值。

​ 2.在这个函数中,可以包含任何合法的C++代码,同时你也可以使用各种,googletest提供的断言,来帮助检查某些值的正确性。

​ 3.断言可以决定测试用例的结果;如果在测试用例中的任何断言失败(不论是致命失败还是非致命失败),都会导致测试用例结果的失败,否则测试用例执行结果成功;

TEST(TestSuiteName, TestName) {
  ... test body ...
}

TEST()的参数,第一个参数是test suite的名字,第二个参数是test的名字,两个参数必须符合C++命名规范。他们均不建议包含下划线(_)。一个测试用例的完成名称是由包含它的test suite和它自己的名字组成的。来自不同测试套的测试用例可以拥有相同的名字。

我们现在使用如下的一个简单例子进行说明

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

一个测试套可能如下:

// Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
  EXPECT_EQ(Factorial(0), 1);
}

// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
  EXPECT_EQ(Factorial(1), 1);
  EXPECT_EQ(Factorial(2), 2);
  EXPECT_EQ(Factorial(3), 6);
  EXPECT_EQ(Factorial(8), 40320);
}

googletest通过test suite来组织测试用例的相关结果,所以,逻辑上相同的测试用例应该在同一个test suite当中。换言之,TEST()的第一个参数应当相同,在上面的例子中我们拥有两个测试用例,HandlesZeroInput,HandlesPositiveInput,同属于同一个测试套FactorialTest.

当命名你的测试用例和测试套时应遵循如下建议:
naming functions and classes.

Test Fixtures:使用相同数据配置的多个测试用例

如果你发现你想写两个或者更多的测试用例,但这些用例使用类似的数据,你可以使用一个test fixture.这可以允许你再几个不同的用例中复用相同的配置。

如何创建一个fixture:

​ 1.创建一个类,继承::testing::Test。并且使用protected:,开始函数体,因为,我们需要让fixture成员再子类中继续使用。

​ 2.再类中,声明任意你计划使用的相关对象。

​ 3.如果有必要,写一个默认的构造函数,或Setup()函数,这个函数为每一个测试用例进行了数据准备工作。一个经常犯的错误是把SetUp()拼写为Setup()函数,因此,建议使用override来确保拼写正确。

​ 4.如果需要的话,我们同样可以书写一个析构函数或者TearDown()函数去释放一些在SetUp()函数中创建的一些资源。怎样创建和使用构造,析构函数,请参见FAQ

​ 5.如果需要,也可以为自己的测试用例创建子例程。

当我们使用fixture时,我们将会使用TEST_F()来代替 TEST(),去创建对象和子进程。

TEST_F(TestFixtureName, TestName) {
  ... test body ...
}

使用TEST_F()与TEST()的使用类似,但是第一个名字必须是test fixture类的类名,你也可以认为_F代表fixture。

令人遗憾的是,C++的宏系统不允许我们使用同一个宏去处理两种类型的测试用例。因此,错误的使用了宏将会导致编译错误。

因此,在使用TEST_F()前,我们必须先定义fixture类否则,我们将会得到一个编译错误:“virtual outside class declaration”.

对于每一个TEST_F()所定义的测试用例,googletest会同时创建一个全新的test fixture,同时立即使用它的SetUp函数进行初始化,然后运行测试,通过TearDown()进行环境清理,然后删除test fixture。请注意,在同一测试套中的测试用例使用的是不同的test fixture对象,googletest会在生产下一个测试用例的test fixture前删除前一个用例的test fixture,googletes从不为多个测试用例重用test fixture。所有测试用例对test fixture的改变,都不会影响其他测试用例的执行。

以下是一个例子

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

首先先定一个一个名为Foo的test fixture类

class QueueTest : public ::testing::Test {
 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_;
};

在这里我们无需定义TearDown()方法,因为析构函数已经替我们在每个测试之后进行了必要的环境清理工作了。

目前我们可以开始书写相关测试用例了

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

当这些测试用例运行时,会发生下面的相关事项

    1. googletest 构造了一个QueueTest对象(我们暂时简称为t1);
    1. t1.SetUp()初始化t1;
    1. 第一个测试用例在t1上运行;
    1. t1.TearDown()开始清理测试环境;
    1. t1对象被销毁;
    1. 上述步骤,在其他用例执行时进行重复。

附注

使用ASSERT和EXPECT区别

例1

TEST(TestName, Subtest_1) 
{
     ASSERT_EQ(1, 1);
     cout<<"after assertions"<<endl;
}

RUN后面会执行打印"after assertions", 如果改成ASSERT_EQ(1, 2),则不会打印"after assertions".

例2

TEST(TestName, Subtest_1) 
{
     EXPECT_EQ(1, 1);
     cout<<"after assertions"<<endl;
}

RUN后面会执行打印"after assertions", 如果改成ASSERT_EQ(1, 2),在错误信息后面同样会打印"after assertions".

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值