Google Test Sample05:Test Fixture的基类和子类
一、环境信息
- Visual Studio 2019
- Windows 10
- 前导知识:Google Test Sample01的学习总结、Google Test Sample03的学习总结
二、Google Test Sample05
1. 示例概述
1.1 sample05在01、03两个示例的基础上展示了如何继承Test Fixture,对已有的Test Fixture进行继承,往往发生在如下两个场景 (1) 定义的Test Fixture可用于指定的一套单元测试用例集,当有多套测试用例时,如何处理?(2) 当不同的测试用例集对应的测试资源基本相同、但又存在差异,又如何处理?
1.2 被继承的Test Fixture在Google Test 中被称为 Super Fixture、继承者被称为sub fixture。Super Fixture /Test Fixture 本质就是一个类,类当然可以被继承
1.3 sample05需要用到 sample01.h + sample01.cpp, sample03.h,文件结构如下
1.4 sample01.cpp 包含Factorial() 、IsPrime()两个函数的具体实现
#include "sample01.h"
// Returns n! (the factorial of n). For negative n and zero, n! is defined to be 1.
int Factorial(int n)
{
int result = 1;
for (int i = 1; i <= n; i++)
{
result *= i;
}
return result;
}
// Returns true if and only if n is a prime number.
bool IsPrime(int n) //素数定义: 在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数
{
// Trivial case 1: small numbers
if (n <= 1) return false;
// Trivial case 2: even numbers
if (n % 2 == 0) return n == 2; //目前不清楚实现原理,当n=2 返回True;当n=4 6.., 返回False
//if (n % 2 == 0) return false; //wrong code
// Now, we have that n is odd and n >= 3.
// Try to divide n by every odd number i, starting from 3
for (int i = 3; ; i += 2)
{
// We only have to try i up to the square root of n
if (i > n / i) break;
// Now, we have i <= n/i < n.
// If n is divisible by i, n is not prime.
if (n % i == 0) return false;
}
// n has no integer factor in the range (1, n), and thus is prime.
return true;
}
1.5 sample01.h 对Factorial(), IsPrime()函数进行了声明
#ifndef sample_01
#define sample_01
// Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int Factorial(int n);
// Returns true if and only if n is a prime number.
bool IsPrime(int n);
#endif // ! sample_01
1.6 sample03.h 完整定义了类Queue(包含成员函数的具体实现)
#ifndef GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
#define GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
//#include <stddef.h>
//using namespace std;
// Queue is a simple queue implemented as a singled-linked list.
// The element type must support copy constructor.
template <typename E> class Queue; // E is the element type
// QueueNode is a node in a Queue, which consists of an element of type E and a pointer to the next node.
template <typename E> class QueueNode // QueueNode是一个队列中的结点,结点中包含 类型为E的元素 和 指向下一个结点的指针
{
friend class Queue<E>; //Queue是QueueNode的友元类,可以访问QueueNode的所有成员
private:
E element_;
QueueNode* next_; //指向类的指针,是自己少见多怪吧
// Creates a node with a given element value. The next pointer is set to NULL.
explicit QueueNode(const E &an_element) :element_(an_element), next_(nullptr) {} //构造函数 //explicit?
// We disable the default assignment operator and copy c'tor. //why disable?
const QueueNode& operator= (const QueueNode&);
QueueNode(const QueueNode&);
public:
const E &element() const // Gets the element in this node. 返回结点的元素值(类型E的一个引用)
{
return element_;
}
QueueNode* next() //Gets the next node in the queue.
{
return next_;
}
const QueueNode* next() const //为什么出现两个几乎完全一样的 next()呢?
{
return next_;
}
};
template <typename E> // E is the element type.
class Queue
{
private:
QueueNode<E>* head_; // The first node of the queue.
QueueNode<E>* last_; // The last node of the queue.
size_t size_; //The number of elements in the queue. //size_t almost equal to int
// We disallow copying a queue. //why disallow?
Queue(const Queue&);
const Queue& operator = (const Queue&);
public:
Queue() : head_(nullptr), last_(nullptr), size_(0) {} // Creates an empty queue.
~Queue() { Clear(); } //D'tor. Clears the queue. //析构函数,用于清除队列
void Clear() // Clears the queue.
{
if (size_ > 0)
{
// 1. Deletes every node.
QueueNode<E>* node = head_;
QueueNode<E>* next = node->next();
for (; ;)
{
delete node;
node = next;
if (node == nullptr)
break;
next = node->next();
}
// 2. Resets the member variables.
head_ = last_ = nullptr;
size_ = 0;
}
}
size_t Size() const { return size_; } // Gets the number of elements.
// Gets the first element of the queue, or NULL if the queue is empty.
QueueNode<E>* Head() { return head_; }
const QueueNode<E>* Head() const { return head_; } //为什么写两个 Head()? const?
// Gets the last element of the queue, or NULL if the queue is empty.
QueueNode<E>* Last() { return last_; }
const QueueNode<E>* Last() const { return last_; }
// Adds an element to the end of the queue. A copy of the element is created using the copy constructor,
// and then stored in the queue. Changes made to the element in the queue doesn't affect the source
// object, and vice versa. //vice versa means '反之亦然'
void Enqueue(const E &element) //向队列添加元素,不会影响源数据
{
QueueNode<E>* new_node = new QueueNode<E>(element); //Apply for memory through 'new'
// explicit QueueNode(const E &an_element):element_(an_element), next_(nullptr) {}
if (size_ == 0)
{
head_ = last_ = new_node;
size_ = 1;
}
else
{
last_->next_ = new_node;
last_ = new_node;
size_++;
}
}
// Removes the head of the queue and returns it. Returns NULL if the queue is empty.
E* Dequeue() //删除队列的头元素并返回元素值
{
if (size_ == 0)
{
return nullptr;
}
const QueueNode<E>* const old_head = head_;
head_ = head_->next_;
size_--;
if (size_ == 0)
{
last_ = nullptr;
}
E* element = new E(old_head->element());
delete old_head;
return element;
}
// Applies a function/functor on each element of the queue, and returns the result in a new queue.
// The original queue is not affected.
template <typename F> Queue* Map(F function) const
{ //拷贝队列 得到一个新队列
Queue* new_queue = new Queue();
for (const QueueNode<E>* node = head_; node != nullptr; node = node->next_)
{
new_queue->Enqueue( function( node->element() ) ); //直接void Enqueue(const E &element); 就可以了吧,function多此一举吧?
//不是多此一举,在 sample03UnitTest.cpp中就可以看到function()的巧妙使用
}
return new_queue;
}
};
#endif // GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
2. 对应的单元测试用例
2.1 sample05中定义了名为QuickTest的Test Fixture,用来判定用例的执行时间是否超过5秒
class QuickTest : public testing::Test
{//用例从SetUp()调用时启动执行,从TearDown()调用时结束执行,因此期间的时间就是用例执行时间
protected:
time_t start_time_;
void SetUp() override
{
start_time_ = time(nullptr);
}
void TearDown() override
{
const time_t end_time = time(nullptr);
EXPECT_TRUE(end_time - start_time_ <= 5) << "The test took too long.";
}
};
2.2 IntegerFunctionTest继承自Test Fixture QuickTest :解决了文章开头的问题1
class IntegerFunctionTest : public QuickTest{ };
TEST_F(IntegerFunctionTest, Factorial)
{
EXPECT_EQ(1, Factorial(-5)); //每条用例都会判定是否超过5秒
EXPECT_EQ(1, Factorial(-1));
}
2.3 QueueTest也继承自Test Fixture QuickTest,但进行了必要的差异化:解决了文章开头的问题2
class QueueTest : public QuickTest
{
protected:
void SetUp() override
{
// First, we need to set up the super fixture (QuickTest).
QuickTest::SetUp();
// Second, some additional setup for this fixture. //差异化
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// 没有数据需要清理,因此TearDown()不进行重新定义、直接继承即可
// virtual void TearDown() {QuickTest::TearDown(); }
Queue<int> q0_; //差异化
Queue<int> q1_;
Queue<int> q2_;
};
2.4 使用QueueTest的TEST_F,由于继承自QuickTest,同样会判定用例的执行是否超过5秒
TEST_F(QueueTest, DefaultConstructor)
{
EXPECT_EQ(0, q0_.Size());
}
2.5 最后,Google也提到了QueueTest, IntegerFunctionTest 也可以被继承,即:可以进行Test Fixture继承的继承,这种继承没有层数限制
2.6 单元测试用例源码及注释
#include "pch.h"
// This sample teaches how to reuse a test fixture in multiple test
// cases by deriving sub-fixtures from it.
//
// When you define a test fixture, you specify the name of the test
// case that will use this fixture. Therefore, a test fixture can
// be used by only one test case.
//
// Sometimes, more than one test cases may want to use the same or
// slightly different test fixtures. For example, you may want to
// make sure that all tests for a GUI library don't leak important
// system resources like fonts and brushes. In Google Test, you do
// this by putting the shared logic in a super (as in "super class")
// test fixture, and then have each test case use a fixture derived
// from this super fixture.
//
// Test Fixture可用于TEST_F宏,但是测试用例集的名称必须与TestFixture名称一致,但有多套测试用例时,如何处理?
// 当不同的测试用例集对应的测试资源基本相同时,又如何处理?
#include <limits.h>
#include <time.h>
//#include "gtest/gtest.h"
#include "sample01.h"
#include "sample03.h"
namespace {
// In this sample, we want to ensure that every test finishes within
// ~5 seconds. If a test takes longer to run, we consider it a
// failure.
//在5秒钟未完成的测试项,就会被判定为Fail
//
// We put the code for timing a test in a test fixture called
// "QuickTest". QuickTest is intended to be the super fixture that
// other fixtures derive from, therefore there is no test case with
// the name "QuickTest". This is OK.
// QuickTest是super fixture,其他的test fixture可以继承QuickTest
//
// Later, we will derive multiple test fixtures from QuickTest.
class QuickTest : public testing::Test
{
protected:
// The UTC time (in seconds) when the test starts
time_t start_time_;
// Remember that SetUp() is run immediately before a test starts.
// 测试启动时,SetUp()就会执行,因此SetUp()的执行时间作为启动时间
// This is a good place to record the start time.
void SetUp() override
{
start_time_ = time(nullptr);
}
// TearDown() is invoked immediately after a test finishes. Here we
// check if the test was too slow.
void TearDown() override
{
const time_t end_time = time(nullptr);// Gets the time when the test finishes
// Asserts that the test took no more than ~5 seconds.
// Did you know that you can use assertions in SetUp() and TearDown() as well? //秀啊
EXPECT_TRUE(end_time - start_time_ <= 5) << "The test took too long.";
}
};
// We derive a fixture named IntegerFunctionTest from the QuickTest fixture.
// All tests using this fixture will be automatically required to be quick.
// Test Fixture的继承
class IntegerFunctionTest : public QuickTest
{
// We don't need any more logic than already in the QuickTest fixture.
// Therefore the body is empty.
};
// Now we can write tests in the IntegerFunctionTest test case.
// Tests Factorial()
TEST_F(IntegerFunctionTest, Factorial) {
// Tests factorial of negative numbers.
EXPECT_EQ(1, Factorial(-5));
EXPECT_EQ(1, Factorial(-1));
EXPECT_GT(Factorial(-10), 0);
// Tests factorial of 0.
EXPECT_EQ(1, Factorial(0));
// Tests factorial of positive numbers.
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
// Tests IsPrime()
TEST_F(IntegerFunctionTest, IsPrime)
{
// Tests negative input.
EXPECT_FALSE(IsPrime(-1));
EXPECT_FALSE(IsPrime(-2));
EXPECT_FALSE(IsPrime(INT_MIN)); //INT_MIN是边界值
// Tests some trivial cases.
EXPECT_FALSE(IsPrime(0));
EXPECT_FALSE(IsPrime(1));
EXPECT_TRUE(IsPrime(2));
EXPECT_TRUE(IsPrime(3));
// Tests positive input.
EXPECT_FALSE(IsPrime(4));
EXPECT_TRUE(IsPrime(5));
EXPECT_FALSE(IsPrime(6));
EXPECT_TRUE(IsPrime(23));
}
// The next test case (named "QueueTest") also needs to be quick, so
// we derive another fixture from QuickTest.
// 同样继承自QuickTest,但是新创建的Test Fixture需要进行一定的定制化
//
// The QueueTest test fixture has some logic and shared objects in
// addition to what's in QuickTest already. We define the additional
// stuff inside the body of the test fixture, as usual.
class QueueTest : public QuickTest
{
protected:
void SetUp() override
{
// First, we need to set up the super fixture (QuickTest).
QuickTest::SetUp();
// Second, some additional setup for this fixture.
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// By default, TearDown() inherits the behavior of
// QuickTest::TearDown(). As we have no additional cleaning work
// for QueueTest, we omit it here.
// 没有数据需要清理,因此TearDown()不进行重新定义、直接继承即可
// virtual void TearDown() {QuickTest::TearDown(); }
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};
// Now, let's write tests using the QueueTest fixture.
// Tests the default constructor.
TEST_F(QueueTest, DefaultConstructor)
{
EXPECT_EQ(0, q0_.Size());
}
// Tests Dequeue().
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
// If necessary, you can derive further test fixtures from a derived
// fixture itself. For example, you can derive another fixture from
// QueueTest. Google Test imposes no limit on how deep the hierarchy
// can be. In practice, however, you probably don't want it to be too
// deep as to be confusing. //可以进行Test Fixture子继承的继承
2.7 运行结果