Google C++單元測試架構(Gtest)系列教程之四——參數化

來源:互聯網
上載者:User

引言

在上一篇文章中,我們學習了如何使用Gtest的測試韌體(Test fixture)完成測試代碼和測試資料的複用,這一節我們來學習如何使用Gtest值參數化的方法,簡化函數測試;使用型別參數化的方法,簡化對模板類的測試。

值參數化

假設我們要對以下函數進行測試:

// 判斷n是否為質數
bool IsPrime(int n)

假設我們要編寫判定結果為false的測試案例,根據之前學習的斷言和TEST()的使用方法,我們編寫測試代碼如下:

// Tests negative input.
TEST(IsPrimeTest, Negative) {
EXPECT_FALSE(IsPrime(-1));
EXPECT_FALSE(IsPrime(-2));
EXPECT_FALSE(IsPrime(-5));
EXPECT_FALSE(IsPrime(-100));
EXPECT_FALSE(IsPrime(INT_MIN));
}

顯然我們對“EXPECT_FALSE(IsPrime(X))”這樣的語句複製粘貼了5次,但當被測資料有幾十個上百個的時候,再使用複製粘帖的方式就弱爆了。下面我們來看Gtest中為解決這個問題,給我們提供的方法。
首先,我們添加一個繼承自::testing::TestWithParam<T>的類,其中T就是我們被測資料的類型,針對以上函數IsPrimeTest,添加以下類:

class IsPrimeParamTest : public::testing::TestWithParam<int>
{
};

在該類中,我們可以編寫SetUp()和TearDown()函數,分別完成資料初始化和資料清理,還可以添加類成員、其他類成員函數,相關的用法,可以參看Gtest Project的這個例子,這裡我們僅對函數作測試,SetUp()等方法都不需要用到,IsPrimeParamTest為一個空的類。

接著我們需要使用宏TEST_P來編寫相應的測試代碼:

TEST_P(IsPrimeParamTest, Negative)
{
int n = GetParam();
EXPECT_FALSE(IsPrime(n));
}

GetParam()方法用於擷取當前參數的具體值,這段測試代碼相比上面的是不是精簡多了?!

最後,我們使用INSTANTIATE_TEST_CASE_P()告知Gtest我們的被測參數都有哪些:

INSTANTIATE_TEST_CASE_P(NegativeTest, IsPrimeParamTest, testing::Values(-1,-2,-5,-100,INT_MIN));

以上第一個參數為測試執行個體的首碼,可以隨意取;第二個參數為測試類別的名稱;第三個參數指示被測參數,test::Values表示使用括弧內的參數。運行該測試案例,得到結果如下:

Running main() from gtest_main.cc
[==========] Running 5 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 5 tests from NegativeTest/IsPrimeParamTest
[ RUN ] NegativeTest/IsPrimeParamTest.Negative/0
[ OK ] NegativeTest/IsPrimeParamTest.Negative/0 (0 ms)
[ RUN ] NegativeTest/IsPrimeParamTest.Negative/1
[ OK ] NegativeTest/IsPrimeParamTest.Negative/1 (0 ms)
[ RUN ] NegativeTest/IsPrimeParamTest.Negative/2
[ OK ] NegativeTest/IsPrimeParamTest.Negative/2 (0 ms)
[ RUN ] NegativeTest/IsPrimeParamTest.Negative/3
[ OK ] NegativeTest/IsPrimeParamTest.Negative/3 (0 ms)
[ RUN ] NegativeTest/IsPrimeParamTest.Negative/4
[ OK ] NegativeTest/IsPrimeParamTest.Negative/4 (0 ms)
[----------] 5 tests from NegativeTest/IsPrimeParamTest (1 ms total)

[----------] Global test environment tear-down
[==========] 5 tests from 1 test case ran. (1 ms total)
[ PASSED ] 5 tests.

從結果上可以看出每個測試執行個體的全稱為:首碼/測試案例名稱.測試執行個體名稱。

型別參數化

像能以參數的形式列出被測值一樣,我們也可以以參數的形式列出被測類型,這極大地方便了對模板類的測試。針對編寫測試代碼前已知被測類型和未知被測類型兩種情況,Gtest還為我們提供了兩種不同的方法,下面假設我們分別使用兩種方法對以下類進行測試:

template <typename E> // E is the element type.
class Queue {
public:
Queue();
void Enqueue(const E& element);
E* Dequeue(); // Returns NULL if the queue is empty.
size_t size() const;
...
};

方法一:已知被測類型

對於以上模板類Queue,假設我們在編寫測試代碼之前已知需要對其作int和char類型的測試,首先我們需要編寫產生具體類型的QueueFactory 方法:

template <class T>
Queue<T>* CreateQueue();
template <>
Queue<int>* CreateQueue<int>()
{
return new Queue<int>;
}
template <>
Queue<char>* CreateQueue<char>()
{
return new Queue<char>;
}

然後我們需要編寫測試韌體類模板(test fixture class template):

template <class T>
class QueueTest:public testing::Test
{
protected:
QueueTest():queue(CreateQueue<T>()){}
virtual ~QueueTest(){delete queue;}
Queue<T>* const queue;
};

可以看到QueueTest的建構函式中使用了Factory 方法對類成員變數queue進行初始化。然後我們需要聲明和註冊我們要測試的類型:

using testing::Types;
// The list of types we want to test.
typedef Types<int, char> Implementations;

TYPED_TEST_CASE(QueueTest, Implementations);

這裡又用到了一個新的宏:TYPED_TEST_CASE(TestCaseName, TypeList),第一個參數為測試案例名稱,第二個參數為類型列表。最後我們使用TYPED_TEST宏編寫檢測代碼:

// 檢測對象產生後,queue的大小是否為0
TYPED_TEST(QueueTest, DefaultConstructor) {
EXPECT_EQ(0u, this->queue->Size());
}

編譯、運行該測試程式,得到測試結果如下:

Running main() from gtest_main.cc
[==========] Running 2 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 1 test from QueueTest/0, where TypeParam = int
[ RUN ] QueueTest/0.DefaultConstructor
[ OK ] QueueTest/0.DefaultConstructor (0 ms)
[----------] 1 test from QueueTest/0 (1 ms total)

[----------] 1 test from QueueTest/1, where TypeParam = char
[ RUN ] QueueTest/1.DefaultConstructor
[ OK ] QueueTest/1.DefaultConstructor (0 ms)
[----------] 1 test from QueueTest/1 (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 2 test cases ran. (1 ms total)
[ PASSED ] 2 tests.


方法二:未知類型

在編寫測試案例的時候,我們可能並不知道該Queue類會被哪些類型執行個體化、被測類型是什麼,如此是否就無法編寫測試案例?!安心する,Gtest為我們提供了方法,可讓我們先寫檢測案例,具體要測的類型可以後期補上,這種方法如下:

與方法一相同,我們需要使用產生Queue的Factory 方法,並定義韌體類模板,這裡圖方便,繼承了以上QueueTest模板類。

template <class T>
class QueueTest2:public QueueTest<T>{
};

接下來,使用宏TYPED_TEST_CASE_P聲明測試案例,其參數為韌體模板類的名稱。

TYPED_TEST_CASE_P(QueueTest2);

然後我們就可以使用宏TYPED_TEST_P編寫測試案例了:

// 檢測對象產生後,queue的大小是否為0
TYPED_TEST_P(QueueTest2, DefaultConstructor) {
EXPECT_EQ(0u, this->queue->Size());
}

相比方法一,該方法還多了一步:註冊測試執行個體。這裡需要使用到REGISTER_TYPED_TEST_CASE_P宏:

REGISTER_TYPED_TEST_CASE_P(QueueTest2, DefaultConstructor);

通過以上基本,我們大部分的未知類型測試代碼都編寫完成了,但我們並沒有一個真正意義上的測試執行個體,因為我們還沒有指定測試類型。通常我們將以上測試代碼寫進一個.h標頭檔中,任何想要使用具體類型執行個體化的代碼都可以#include該標頭檔。
假設我們要測試int和char類型,我們可以在一個.cc檔案中編寫以下代碼:

typedef Types<int, char> Implementations;

INSTANTIATE_TYPED_TEST_CASE_P(QueueInt_Char, QueueTest2, Implementations);

同樣,我們需要使用Types列出被測類型,注意到這裡列出被測類型是在使用TYPED_TEST_P編寫測試執行個體之後。

小結

這一節我們學習了如何使用Gtest值參數化的方法簡化函數測試,如何使用已知型別參數化、未知型別參數化的方法簡化對模板類的測試。Gtest project中給了我們一個值和類型均為自訂類的例子,感興趣的話可以猛擊這裡。

Reference:googletest project

             《玩轉Google開源C++單元測試架構Google Test系列(gtest)》by CoderZh

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.