Google Test Sample03_Test Fixture
-
- 一、環境資訊
- 二、Google Test Sample03
-
- 1. Test Fixture簡介
- 2. 單元測試用例
- 3. 一個阻塞問題(已解決)
一、環境資訊
- Visual Studio 2019
- Windows 10
- C++模闆介紹
二、Google Test Sample03
1. Test Fixture簡介
1.1 Google Test sample03主要示範了 Test Fixture的使用
1.2 Fixtures are fittings or furniture which belong to a building and are legally part of it, for example, a bathtub or a toilet. 對于Test Fixture就是每個測試用例執行時都要用到的相同的測試資源,如果對每個測試用例都單獨進行編碼、用于準備測試資源,代碼備援量太大,是以Test Fixture對于 code sharing意義重大
1.3 使用Test Fixture,需要繼承testing::Test類:說白了就是定制化自己的 void SetUp( ) 和 virtual void TearDown( ),使得不同用例可以使用相同的測試資源 及 複用相同的代碼
//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.
// s.t.全稱subject to,意思是使得...滿足...
Queue<int> q0;// Declares the variables your tests want to use.
Queue<int> q1; // 建立三個 sample03.h中定義的隊列
Queue<int> q2;
// virtual void SetUp() will be called before each test is run. //Google Test每一條用例執行前都會調用 void SetUp()
// Youshould define it if you need to initialize the variables.
// Otherwise, this can be skipped.
void SetUp() override //override?
{
q1.Enqueue(1); //向隊列中添加元素
q2.Enqueue(2);
q2.Enqueue(3);
}
// virtual void TearDown() will be called after each test is run. //Google Test每一條用例執行後都會調用 virtual void TearDown()
// You should define it if there is cleanup work to do. //TearDown用來清理資料
// Otherwise,you don't have to provide it.
//virtual void TearDown() { }
};
1.4 TEST Fixture需要使用宏 TEST_F,而且 測試用例集名字 必須和定義的類名保持一緻
class QueueTest : public testing::Test{//realization };
TEST_F(QueueTest, DefaultConstructor){//realization}
1.5 各個測試用例TEST_F()使用相同的測試資源,測試資源不會受到上一條測試用例的影響
2. 單元測試用例
2.1 在單元測試用例中,首先定義了類Queue ,随後使用Test Fixture對類進行了測試,可以看到:所有的測試用例TEST_F()在執行時均調用了相同的測試資源
2.2 如下代碼已經注釋并調試通過,歡迎大家指導
//#ifndef GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
//#define GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
#include <stddef.h> //template
#include "pch.h" // #include "gtest/gtest.h"
// 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的友元類,是以Queue可以通路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
{ //在每一個隊列元素上 運用指定函數 function,并将函數結果儲存到新隊列
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多此一舉吧?
//不是多此一舉,在 196行中就可以看到function()的具體使用
}
return new_queue;
}
};
//#endif // GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
//To use a test fixture, derive a class from testing::Test.
class QueueTest : public testing::Test
{
protected: // You should make the members protected s.t. they can be accessed from sub-classes.
// s.t.全稱subject to,意思是使得...滿足...
Queue<int> q0;// Declares the variables your tests want to use.
Queue<int> q1; // 建立三個 sample03.h中定義的隊列
Queue<int> q2;
// virtual void SetUp() will be called before each test is run. //Google Test每一條用例執行前都會調用 void SetUp()
// Youshould define it if you need to initialize the variables.
// Otherwise, this can be skipped.
void SetUp() override //override?
{
q1.Enqueue(1); //向隊列中添加元素
q2.Enqueue(2);
q2.Enqueue(3);
}
// virtual void TearDown() will be called after each test is run. //Google Test每一條用例執行後都會調用 virtual void TearDown()
// You should define it if there is cleanup work to do. //TearDown用來清理資料
// 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(). //測試類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.
// 産生了一個新隊列,和源隊列相比,新隊列的每一個元素值都是源隊列對應元素值的兩倍,這就是function的作用
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()); //為什麼要把單元測試用例寫在類的聲明中呢? //因為下面的測試用例會調用 見233行
// 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()); //感覺代碼寫的真棒,這還是人家Google 2005年的例子
}
delete new_q;
}
};
// When you have a test fixture, you define a test using TEST_F instead of TEST.
// TEST Fixture使用宏 TEST_F
// Tests the default c'tor. //1.測試預設的構造函數(每條用例結束,會執行析構函數嗎?)
TEST_F(QueueTest, DefaultConstructor) //注意:測試用例集名字 必須和定義的類名保持一緻
{
// You can access data in the test fixture here.
EXPECT_EQ(0, q0.Size()); //這裡的結果應該是Pass,因為SetUP()中 q0 未添加資料
}
// Tests Dequeue().
TEST_F(QueueTest, Dequeue) //2.測試Dequeue()函數
{
int* n = q0.Dequeue();
EXPECT_TRUE(n == nullptr); //測試結果Pass,因為SetUP()中 q0_ 未添加元素
n = q1.Dequeue(); //Dequeue()的傳回值是一個E類型的指針
ASSERT_TRUE(n != nullptr); //pass, SetUP()中 q1 中添加了一個元素
EXPECT_EQ(1, *n); //q1.Enqueue(1); pass, 元素值就是1
EXPECT_EQ(0, q1.Size()); //彈出一個元素後,隊列的元素個數為0; 0 equal to 0u, 0u?
delete n;
n = q2.Dequeue();
ASSERT_TRUE(n != nullptr);
EXPECT_EQ(2, *n);
EXPECT_EQ(1, q2.Size()); //1u equal to 1
delete n;
}
// Tests the Queue::Map() function.
TEST_F(QueueTest, MapTest) //3.測試Queue::Map()函數(其中包括Enqueue()函數)
{
MapTester(&q0); //傳入的實際是隊列的引用
MapTester(&q1);
MapTester(&q2);
EXPECT_EQ(0,q0.Size());
EXPECT_EQ(1, q1.Size());
EXPECT_EQ(2, q2.Size()); //各個測試用例TEST_F()使用相同的測試資源,測試資源不會受到上一條測試用例的影響
}
TEST_F(QueueTest, OtherTest)
{
EXPECT_FALSE(q0.Size());
EXPECT_TRUE(q1.Size());
}
//} // namespace
3. 一個阻塞問題(已解決)
3.1 sample03.h定義了類Queue(可編譯通過),但是對應的單元測試用例 sample03UnitTest.cpp會編譯失敗,有如下報錯(無論這兩個檔案是否分屬于同一個項目,編譯時均有相同報錯)
#include "sample03.h"
#include "pch.h"
Queue<int> q0;
3.2 為了規避上述問題,将sample03.h 和 sample03UnitTest.cpp合并成一個檔案才編譯通過。這個問題後面有空了還是需要分析排查下具體的原因
3.3 原因分析和解決方法
3.3.1 原因分析:sample03UnitTest.cpp中包含了多個頭檔案,當sample03.h位于pch.h上方時,一定會觸發上述報錯
#include "sample03.h"
#include "pch.h"
3.3.2 解決方法:調整頭檔案的包含順序
#include "pch.h"
#include "sample03.h"
3.3.3 運作結果