目录
- 待测文件
- 测试文件
- 小结
待测文件
sample3
通过一个链表实现了一个队列(Queue)功能,队列的每个节点都是一个QueueNode对象. 链表没有虚拟头节点,head_
指向头节点,last_
指向尾节点.
对于Queue
类,实现了:
- 构造、析构;
- 插入元素(Enqueue)到队列末尾,从队列头弹出(Dequeue)元素;
- 队列数据清除(Clear);
- 队列大小的访问(Size);
- 队列头元节点的访问(Head),尾节点的访问(Last);
- 构造一个新的队列(Map),与原队列的每个元素作为方法的参数,方法调用结果作为新队列元素逐个插入.
对于QueueNode
类,实现了:
- 模板类型的元素值;
- 指向下一个节点的指针.
"-inl" 表明这是个声明和实现的混合文件.
sample3-inl.h
#include <stddef.h>// Queue is a simple queue implemented as a singled-linked list.
//
// The element type must support copy constructor.
template <typename E> // E is the element type
class Queue;// 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> // E is the element type
class QueueNode {friend class Queue<E>;public:// Gets the element in this node.const E& element() const { return element_; }// Gets the next node in the queue.QueueNode* next() { return next_; }const QueueNode* next() const { return next_; }private:// 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) {}// We disable the default assignment operator and copy c'tor.const QueueNode& operator=(const QueueNode&);QueueNode(const QueueNode&);E element_;QueueNode* next_;
};template <typename E> // E is the element type.
class Queue {public:// Creates an empty queue.Queue() : head_(nullptr), last_(nullptr), size_(0) {}// D'tor. Clears the queue.~Queue() { Clear(); }// Clears the queue.void Clear() {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;}}// Gets the number of elements.size_t Size() const { return size_; }// 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_; }// 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.void Enqueue(const E& element) {QueueNode<E>* new_node = new QueueNode<E>(element);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()));}return new_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.// We disallow copying a queue.Queue(const Queue&);const Queue& operator=(const Queue&);
};
测试文件
sample3_unittest.cc
#include "sample3-inl.h"
#include "gtest/gtest.h"
namespace {
// To use a test fixture, derive a class from testing::Test.
class QueueTestSmpl3 : public testing::Test {protected: // You should make the members protected s.t. they can be// accessed from sub-classes.// virtual void SetUp() will be called before each test is run. You// should define it if you need to initialize the variables.// Otherwise, this can be skipped.void SetUp() override {q1_.Enqueue(1);q2_.Enqueue(2);q2_.Enqueue(3);}// virtual void TearDown() will be called after each test is run.// You should define it if there is cleanup work to do. Otherwise,// you don't have to provide it.//// virtual void TearDown() {// }// A helper function that some test uses.static int Double(int n) { return 2 * n; }// A helper function for testing Queue::Map().void MapTester(const Queue<int>* q) {// Creates a new queue, where each element is twice as big as the// corresponding one in q.const Queue<int>* const new_q = q->Map(Double);// Verifies that the new queue has the same size as q.ASSERT_EQ(q->Size(), new_q->Size());// Verifies the relationship between the elements of the two queues.for (const QueueNode<int>*n1 = q->Head(), *n2 = new_q->Head();n1 != nullptr; n1 = n1->next(), n2 = n2->next()) {EXPECT_EQ(2 * n1->element(), n2->element());}delete new_q;}// Declares the variables your tests want to use.Queue<int> q0_;Queue<int> q1_;Queue<int> q2_;
};// When you have a test fixture, you define a test using TEST_F
// instead of TEST.// Tests the default c'tor.
TEST_F(QueueTestSmpl3, DefaultConstructor) {// You can access data in the test fixture here.EXPECT_EQ(0u, q0_.Size());
}// Tests Dequeue().
TEST_F(QueueTestSmpl3, Dequeue) {int* n = q0_.Dequeue();EXPECT_TRUE(n == nullptr);n = q1_.Dequeue();ASSERT_TRUE(n != nullptr);EXPECT_EQ(1, *n);EXPECT_EQ(0u, q1_.Size());delete n;n = q2_.Dequeue();ASSERT_TRUE(n != nullptr);EXPECT_EQ(2, *n);EXPECT_EQ(1u, q2_.Size());delete n;
}// Tests the Queue::Map() function.
TEST_F(QueueTestSmpl3, Map) {MapTester(&q0_);MapTester(&q1_);MapTester(&q2_);
}
} // namespace
sample3的测试并没有像sample2那样,在测试用例中定义测试对象,然后进行一系列操作后再断言测试,而是用了一个集成自testing::Test
的测试夹具类QueueTestSmpl3
,包装了对测试对象的一系列操作,后续测试不需要重复编写.
TEST_IF
:用测试夹具测试时,定义测试用例不用TEST
,而是用TEST_IF
.
ASSERT_TRUE
:功能类似EXPECT_TRUE
,但断言不通过时,前者会导致测试不会继续,后者会继续.
对于QueueTestSmpl3
类,
- 定义了3个待测的
Queue<int>
对象q0_, q1_, q2_
,并用父类testing::Test
的派生方法进行初始化; - Setup():派生自testing::Test,会在执行测试用例之前运行. 如果有数据初始化工作,可以在这里进行;
- MapTester():包装对
Queue::Map()
的测试操作.
对于Queue
和QueueNode
,并没有对每个public方法设计测试用例,而是让测试用例覆盖其成员方法.
- DefaultConstructor:测试默认构造函数,包含了ctor和Enqueue();
- Dequeue:测试出队列方法;
- Map:利用测试夹具中定义的MapTester方法,对Queue::Map进行测试.
当然,这里其实没有覆盖对Queue::Clear()
方法的测试.
小结
利用测试夹具类(继承自testing::test)封装对待测类对象的初始化,可自定义测试方法 封装对待测对象的一系列操作.