这篇来学习Gtest官方示例中的第5个,为什么直接跳过第4个,因为第四个是测试一个简单的计数器,看了下单元测试内容,没有新的知识点,就一个TEST,里面使用了连续3 4个EXPECT_TRUE断言宏。完全没有任何新的知识点,就不再介绍第4个例子,直接来看第五个。第五个是介绍test fixture可以通过继承使用父类中的测试夹具。
1.场景
这个例子教我们如何在多个测试用例中复用测试夹具:通过派生子夹具。当你定义好了一个测试夹具,你指定一个测试用例使用这个测试夹具。一个测试夹具只能被一个测试用例使用。某些时候,更多的测试用例希望使用相同或有一点不同的测试夹具。例如,你可能想要确保GUI库的所有测试都不会泄漏系统资源,例如字体和画笔。在Gtest中,你可以这样做,把共享逻辑放入一个超类的测试夹具中。每个测试用例使用到的测试夹具从这个超类中派生。
不同的测试用例如果是共同的数据和逻辑,直接从父类继承就好,如果有需要不同的,可以通过重写父类的SetUp()或者TearDown()方法。
2.第5个示例
第五个示例没有单独的头文件和实现文件,测试文件都是测试sample01和sample03中的内容。
TestSample05.cpp
#include <limits.h>
#include <time.h>
#include "gtest/gtest.h"
#include "sample01.h"
#include "sample03.h"
namespace {
// 在这次示例中, 我们要确保每个测试必须在5秒内结束
//如果有超过5秒,我们把这个测试判为失败
//
// 我们把在测试夹具中计算代码运行时间叫做"QuickTest". QuickTest 被设计为一个超夹具类,这样其他夹具可以从这个类派生.
//
// 之后,我们将从QuickTest派生多个测试夹具类
class QuickTest : public testing::Test {
protected:
// 提醒一下:SetUp() 是在一个test执行之前就会执行
// 这是一个好地方去记录开始时间
void SetUp() override { start_time_ = time(nullptr); }
// TearDown() 是在每个测试执行结束之后才会被执行.
// 这里我们检查下这个测试是不是运行太慢(大于5秒)
void TearDown() override {
// 当测试结束获取结束时间
const time_t end_time = time(nullptr);
// 断言测试运行没有超过5秒. Did you
// 你是否知道在 SetUp() 和TearDown()也可以使用测试断言
EXPECT_TRUE(end_time - start_time_ <= 5) << "The test took too long.";
}
// UTC 时间(秒为单位)
time_t start_time_;
};
// 我们从QuickTest类派生一个夹具叫IntegerFunctionTest
// 使用此夹具的所有测试将自动进行quick test
class IntegerFunctionTest : public QuickTest {
// 这里我们不再需要更多的逻辑,直接继承来自父类QuickTest 的测试夹具
// 因此,这个类的是一个空的
};
// 现在我们可以写测试用例,用夹具:IntegerFunctionTest
// 测试阶乘
TEST_F(IntegerFunctionTest, Factorial) {
// 测试负数的阶乘
EXPECT_EQ(1, Factorial(-5));
EXPECT_EQ(1, Factorial(-1));
EXPECT_GT(Factorial(-10), 0);
// 测试0的阶乘
EXPECT_EQ(1, Factorial(0));
// 测试正整数的阶乘
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
// 测试是否为素数
TEST_F(IntegerFunctionTest, IsPrime) {
// 测试负数是否为素数
EXPECT_FALSE(IsPrime(-1));
EXPECT_FALSE(IsPrime(-2));
EXPECT_FALSE(IsPrime(INT_MIN));
// 测试几个简单的数字是否为素数
EXPECT_FALSE(IsPrime(0));
EXPECT_FALSE(IsPrime(1));
EXPECT_TRUE(IsPrime(2));
EXPECT_TRUE(IsPrime(3));
// 测试正整数是否为素数
EXPECT_FALSE(IsPrime(4));
EXPECT_TRUE(IsPrime(5));
EXPECT_FALSE(IsPrime(6));
EXPECT_TRUE(IsPrime(23));
}
// 下一个测试用例 (叫 "QueueTest") 也需要更快执行完成, so
// 所以我们这里也从QuickTest.派生另外一个测试夹具
//
// QueueTest的测试夹具已经可以继承来自父类的测试夹具.
// 通常,如果还有不同,我们这里定义下额外的,所以需要重写父类的SetUp方法
class QueueTest : public QuickTest {
protected:
void SetUp() override {
// 第一个需要初始化一下父类 (QuickTest)中的SetUp()
QuickTest::SetUp();
// 第二, 一些额外的初始化操作.
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// 默认TearDown()的行为继承来自QuickTest::TearDown()
// 这里我们没有额外的清除操作,在QueueTest我们把TearDown()代码给注销
//
// virtual void TearDown() {
// QuickTest::TearDown();
// }
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};
//现在让我们用 QueueTest 的测试夹具来写测试
// 测试默认的构造
TEST_F(QueueTest, DefaultConstructor) {
EXPECT_EQ(0u, q0_.Size());
}
// 测试队列新增元素
TEST_F(QueueTest, Dequeue) {
int* n = q0_.Dequeue();
EXPECT_TRUE(n == nullptr);
n = q1_.Dequeue();
EXPECT_TRUE(n != nullptr);
EXPECT_EQ(1, *n);
EXPECT_EQ(0u, q1_.Size());
delete n;
n = q2_.Dequeue();
EXPECT_TRUE(n != nullptr);
EXPECT_EQ(2, *n);
EXPECT_EQ(1u, q2_.Size());
delete n;
}
} // namespace
//如果有必要,你可以派生更多的测试夹具,例如,你可以从 QueueTest派生另外一个夹具
// Google测试框架对派生层次结构的深度没有限制
// 但是在实践中,你可能不希望它也是如此,因为容易混淆。
运行结果
总结:
如果想多个测试用例使用类似相同的测试夹具,那么可以从一个超类中派生出不同的子测试夹具,通过这些子测试夹具,可以方便测试不同的测试用例。