Google Test 入門(二)

本篇主要介紹Google Test 的測試示例。

TEST()宏

TEST()參數

TEST(TestSuiteName, TestName) {
  ... test body ...
}

TEST()的參數按從一般到具體進行設置,第一個參數是Test Case 的名稱,第二個參數是(隸屬於第一個Test Case參數的)Test 的名稱。這兩個名字都必須是有效的 C++ 標識符,且它們都不應該包含下劃線(_)。一個測試的完整名稱包括 Test Case 名稱及 Test 的名稱,不同 Test CaseTest 名稱可以相同。

當命名你的測試套件和測試時,你應該遵循命名函數和類相同的規則

googletest 根據 Test Case對測試結果進行分組,所以一些邏輯上相關的 Test應當放入同一個 Test Case中。換句話說,它們的 TEST() 的第一個參數應該是相同的。

TEST()測試示例

創建一個測試的步驟:

  1. 使用 TEST() 宏定義並命名一個測試函數。這些是普通的沒有返回值的 C++ 函數。
  2. 在這個函數中,可以包含任何你想包含的有效的 C++ 語句,使用各種 googletest 斷言檢查值。
  3. 測試的結果由斷言決定;如果測試中的任何斷言失敗了(致命的或非致命的),或者如果測試崩潰了,則整個測試失敗。否則,測試成功。

使用TEST() 宏來編寫一個測試程序,示例代碼如下:

#include "gtest/gtest.h"

//==================================
// 被測試函數
//==================================
// 此函數用於判斷入參是否爲正整數:如果是,則返回0;否則,返回-1
int Positive(int nNum)
{
    if (nNum > 0)
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

//==================================
// 上述函數的Test Suite看起來可能像下面這樣
//==================================
// 測試入參“2”、“4”、“5”是否爲正整數
TEST(PositiveTest, HandlesPositiveInput)
{
    EXPECT_EQ(Positive(2), 0);
    EXPECT_EQ(Positive(4), 0);
    EXPECT_EQ(Positive(5), 0);
}
 
// 測試入參“0”是否爲正整數
TEST(PositiveTest, HandlesZeroInput)
{
    EXPECT_EQ(Positive(0), -1);
}
 
// 測試入參“-2"、”-5“是否爲正整數
TEST(PositiveTest, HandlesNegativeInput)
{
    EXPECT_EQ(Positive(-2), -1);
    EXPECT_EQ(Positive(-5), -1);
}

//==================================
// main函數
//================================== 
int main(int argc, char **argv)
{
    // 分析gtest程序的命令行參數
    testing::InitGoogleTest(&argc, argv);
 
    // 調用RUN_ALL_TESTS()運行所有測試用例
    // main函數返回RUN_ALL_TESTS()的運行結果
    return RUN_ALL_TESTS();
}

在上述代碼中,我們編寫了三個test,分別爲HandlesPositiveInput、HandlesZeroInput 和 HandlesNegativeInput,這三個test 都屬於同一個Test Case(PositiveTest)。

編譯並執行上述代碼,結果類似於下面:

TEST()測試結果

TEST_F()宏

Test Fixture(測試夾具):多個測試使用相同的數據配置。
如果你發現你寫了兩個或更多測試操作類似的數據,你可以使用一個測試夾具。它允許你爲多個不同的測試複用相同的對象配置。

TEST_F()參數

當使用測試夾具時,使用 TEST_F() 而不是 TEST(),它允許你訪問測試夾具中的對象和子例程(你可能已經猜到:_Ffixture):

TEST_F(TestFixtureName, TestName) {
  ... test body ...
}

TEST() 一樣,第一個參數是測試套件的名字,但對於 TEST_F(),這必須是Test Fixture類的名字。

TEST_F()測試

創建一個測試夾具:

  1. 派生一個繼承 ::testing::Test 的類,並將該類中的一些內容聲明爲 protected 類型,以便在子類中對夾具中的成員進行訪問;
  2. 在類內部聲明任何你打算使用的對象;
  3. 根據實際情況,編寫默認的構造函數或SetUp()函數,來爲每個 test 準備所需內容(一個常見的錯誤是把 SetUp() 拼成了 Setup());
  4. 根據實際情況,編寫默認的析構函數或TearDown()函數,來釋放SetUp()中分配的資源(要學習何時你應該使用構造函數/析構函數以及何時你應該使用SetUp()/TearDown(),請閱讀 FAQ。);
  5. 根據實際情況,定義 test 共享的子程序。

當使用Test Fixture時,使用 TEST_F() 而不是 TEST()TEST_F()允許你訪問Test Fixture中的對象和子例程。

你必須在使用 TEST_F()之前先定義一個Test Fixture類,否則你將得到一個編譯器錯誤 “virtual outside class declaration”。

對於通過 TEST_F() 定義的每個test,googletest 將在運行時創建一個 全新的test fixture,並立即通過 SetUp() 對其進行初始化,然後運行test,之後通過調用 TearDown() 進行數據清理,最後刪除test fixture。注意: 同一個 test case 中不同的 test 具有不同的 test fixture 對象,並且 googletest 每次創建新的 test fixture 前都會先刪除之前的 test fixture。多個 test 不會重用相同的 test fixture,某個 test 對 fixture 進行的修改對其他 test 無影響。

使用TEST_F()宏編寫一個測試程序,示例代碼如下:


#include "gtest/gtest.h"
//==================================
// 定義Test Fixture 類FooTest
//================================== 
class FooTest: public ::testing::Test {
protected:
    // Code here will be called immediately after the constructor (right before each test)
    void SetUp()
    {
        m_nTarget = 5;
    }
 
    // Code here will be called immediately after each test (right before the destructor)
    void TearDown()
    {
    }
 
public:
    int IsLargeThan5(const int & nNum);
    int m_nTarget;
};

// 判斷入參是否大於5:如果是,則返回0;否則返回-1
int FooTest::IsLargeThan5(const int & nNum)
{
    if (nNum > m_nTarget)
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

//==================================
// 編寫使用TEST_F()和這個test fixture的測試
//==================================  
TEST_F(FooTest, HandlesInput6)
{
    EXPECT_EQ(IsLargeThan5(6), 0);
}
 
TEST_F(FooTest, HandlesInput5)
{
    EXPECT_EQ(IsLargeThan5(5), 0);
}
 
TEST_F(FooTest, HandlesInput4)
{
    EXPECT_EQ(IsLargeThan5(4), -1);
}
 
//==================================
// main函數
//================================== 
int main(int argc, char **argv)
{
    // 分析gtest程序的命令行參數
    ::testing::InitGoogleTest(&argc, argv);
 
    // 調用RUN_ALL_TESTS()運行所有測試用例
    // main函數返回RUN_ALL_TESTS()的運行結果
    return RUN_ALL_TESTS();
}

在上述代碼中,我們編寫了三個 test,分別爲:HandlesInput6、HandlesInput5 和 HandlesInput4,這三個 test 都屬於同一個 test case(即 FooTest)。注意,這裏的 test case(即 FooTest) 一定要是 test fixture 類。

上述代碼中的 test 運行時,主要會進行如下操作:

  1. googletest 構造一個 FooTest 類的對象(我們稱之爲 f1);
  2. f1.SetUp() 函數對 f1 進行初始化;
  3. 使用對象 f1,運行第一個 test(HandlesInput6);
  4. f1.TearDown() 在 test 完成後,進行清理工作;
  5. 對象 f1 被析構。
  6. 上述5個步驟在另一個 FooTest 類的對象(如 f2)中重複,此次會運行 HandlesInput5。

編譯並執行上述代碼,結果如下:
TEST_F()測試結果

調用Test

在上面的代碼示例中我們能夠看到,調用 Test 的操作是通過 RUN_ALL_TESTS() 宏完成的。

TEST()TEST_F() 隱式地把它們的測試註冊給 googletest。因此,不像許多其它的 C++ 測試框架,你無需以運行的順序把你定義的測試重新列出。

定義好test之後,你可以通過RUN_ALL_TESTS() 來運行他們。RUN_ALL_TESTS()宏在所有 test 都成功時,返回0;否則返回1。需要注意的是,RUN_ALL_TESTS() 會運行你的鏈接單元中所有關聯的 test,這些 test 可以來自不同的 test case,甚至不同的源文件。

當我們調用 RUN_ALL_TESTS() 宏的時候,會進行以下操作:

  1. 保存所有 googletest flag 的狀態;
  2. 爲第一個 test 創建一個 test fixture 對象;
  3. 通過 SetUp() 對上一步創建的 test fixture 對象進行初始化;
  4. 使用 test fixture 對象運行 test;
  5. 通過 TearDown() 清理 fixture;
  6. 刪除 fixture;
  7. 還原所有 googletest flag 的狀態;
  8. 爲下一個 test 重複上述操作,直到所有的 test 執行完成。

如果發生了致命失敗則後續的步驟將被跳過。

注意:

  • main() 函數必須要返回 RUN_ALL_TESTS() 宏的結果。這種設計的原理是自動化測試服務是基於測試的退出碼來決定它是否通過的,而不是它的 stdout/stderr 輸出。
  • RUN_ALL_TESTS() 只能運行一次,多次調用會與 googletest 的一些功能(如 thread-safe death tests)發生衝突。

編寫main函數

編寫 main() 函數時,必須要返回 RUN_ALL_TESTS() 宏的值。
::testing::InitGoogleTest()函數將會解析命令行中的 googletest 參數,並移除所有已識別的標記。它允許用戶通過多樣的命令行參數來控制測試程序的行爲(即定製命令行參數的功能)。

需要注意的是,::testing::InitGoogleTest() 函數必須要在 RUN_ALL_TESTS()之前調用,否則對應的 flag 可能不會被正常地初始化。

備註

Google Test 是線程安全的,其線程安全特性要依賴 pthreads 庫。

參考:

github原文
Googletset入門
Google Test 介紹(一)
Google Test 介紹(二)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章