天天看點

gtest基礎使用06:Google Test自帶示例三:Test Fixture

Google Test Sample03_Test Fixture

    • 一、環境資訊
    • 二、Google Test Sample03
      • 1. Test Fixture簡介
      • 2. 單元測試用例
      • 3. 一個阻塞問題(已解決)

一、環境資訊

  1. Visual Studio 2019
  2. Windows 10
  3. 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 如下代碼已經注釋并調試通過,歡迎大家指導

gtest基礎使用06:Google Test自帶示例三:Test Fixture
//#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;
           
gtest基礎使用06:Google Test自帶示例三:Test Fixture

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 運作結果

gtest基礎使用06:Google Test自帶示例三:Test Fixture

繼續閱讀