天天看點

gtest基礎使用08:Google Test自帶示例五:Test Fixture的基類和子類

Google Test Sample05:Test Fixture的基類和子類

    • 一、環境資訊
    • 二、Google Test Sample05
      • 1. 示例概述
      • 2. 對應的單元測試用例

一、環境資訊

  1. Visual Studio 2019
  2. Windows 10
  3. 前導知識: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,檔案結構如下

gtest基礎使用08:Google Test自帶示例五:Test Fixture的基類和子類

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

gtest基礎使用08:Google Test自帶示例五:Test Fixture的基類和子類

繼續閱讀