GoogleTest

紫冰凌發表於2024-04-26

簡單測試

使用TEST()宏,其中第一個引數是測試套件名稱(對應具體功能),第二個引數是測試用例名稱(測試方向,比如測試判斷是否為質數的函式,就有測試小數,測試負數,測試正數這三個反向),二者都必須是合法的C++識別符號,並且不應該包含下劃線。

#include <gtest/gtest.h>

#include "factorial.h"

// Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
    EXPECT_EQ(Factorial(0), 1);
}

// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
    EXPECT_EQ(Factorial(1), 1);
    EXPECT_EQ(Factorial(2), 2);
    EXPECT_EQ(Factorial(3), 6);
    EXPECT_EQ(Factorial(8), 40320);
}

斷言

GoogleTest斷言是類似於函式呼叫的宏,用於測試類或函式的行為。當斷言失敗時,GoogleTest將列印斷言所在的原始檔、行數以及錯誤資訊。

每個斷言都有兩種版本:ASSERT_版本的失敗是致命失敗(錯誤後測試案例裡面剩下的語句不繼續執行),EXPECT_版本的失敗是非致命失敗(錯誤後測試案例裡面剩下的語句繼續執行)。

斷言 驗證條件
EXPECT_TRUE(condition) condition為真
EXPECT_FALSE(condition) condition為假
EXPECT_EQ(val1, val2) val1 == val2
EXPECT_NE(val1, val2) val1 != val2
EXPECT_LT(val1, val2) val1 < val2
EXPECT_LE(val1, val2) val1 <= val2
EXPECT_GT(val1, val2) val1 > val2
EXPECT_GE(val1, val2) val1 >= val2
EXPECT_STREQ(str1, str2) C字串str1和str2相等
EXPECT_STRNE(str1, str2) C字串str1和str2不相等
EXPECT_STRCASEEQ(str1, str2) C字串str1和str2相等,忽略大小寫
EXPECT_STRCASENE(str1, str2) C字串str1和str2不相等,忽略大小寫
EXPECT_FLOAT_EQ(val1, val2) 兩個float值val1和val2近似相等
EXPECT_DOUBLE_EQ(val1, val2) 兩個double值val1和val2近似相等
EXPECT_NEAR(val1, val2, abs_error) val1和val2之差的絕對值不超過abs_error
EXPECT_THROW(statement, exception_type) statement丟擲exception_type型別的異常
EXPECT_ANY_THROW(statement) statement丟擲任何型別的異常
EXPECT_NO_THROW(statement) statement不丟擲任何異常
EXPECT_THAT(val, matcher) val滿足匹配器matcher

斷言宏返回一個ostream物件,可以使用<<運算子輸出自定義的失敗資訊。例如:
EXPECT_TRUE(my_condition) << "My condition is not true";

測試夾具

為什麼需要測試夾具?為了讓多個測試用例共用相同的物件或資料。
要建立一個fixture,只需繼承::testing::Test類,在類中定義要使用的物件,在預設建構函式或SetUp()函式中進行初始化,在解構函式或TearDown()函式中進行清理(釋放資源),此外還可以定義需要共用的函式。如下所示:

// The fixture for testing class Foo.
class FooTest : public ::testing::Test {
protected:
    FooTest() {
        // You can do set-up work for each test here.
    }

    ~FooTest() override {
        // You can do clean-up work that doesn't throw exceptions here.
    }

    void SetUp() override {
        // Code here will be called immediately after the constructor (right before each test).
    }

    void TearDown() override {
        // Code here will be called immediately after each test (right before the destructor).
    }

    // Class members declared here can be used by all tests in the test suite for Foo.
};

要使用fixture類,使用TEST_F()宏而不是TEST()來定義測試。

為什麼必須用TEST_F()?TEST_F()宏繼承fixture類,TEST()繼承::testing::Test。

TEST_F(TestFixtureName, TestName) {
    test body
}

其中TestFixtureName既是fixture類名,也是測試套件名,在測試體中可以使用fixture類定義的資料成員。

對於每一個使用TEST_F()定義的測試,GoogleTest都會建立一個新的 fixture物件,呼叫SetUp()初始化,執行測試,呼叫TearDown()清理,最後刪除fixture物件。同一個測試套件中的不同測試使用不同的fixture物件,因此一個測試所做的改變不影響其他測試。

Gmock(1.7以上的版本才有)

為什麼要使用Gmock?
在測試中使用真實物件有時是不可行的,因為真實物件依賴昂貴的、不可靠的資源(例如資料庫、網路連線等)使得測試變慢或不穩定。模擬物件(mock object)與真實物件實現相同的介面,但可以在執行時指定它將被如何使用(呼叫什麼方法、以什麼引數、呼叫多少次、以什麼順序)以及它應該做什麼(返回什麼值)。

示例

假設正在開發一個畫圖程式。要想測試程式是否正確,可以對比螢幕上的繪製結果和正確的螢幕截圖,但這種方式太繁瑣、難以維護。實際上,在測試中不需要真正呼叫系統介面在螢幕上繪製圖形,只需驗證是否呼叫了正確的介面即可。

假設程式使用的畫圖介面Turtle如下(類似於Python的turtle模組):
turtle.h

class Turtle {
public:
    virtual ~Turtle() = default;

    virtual void PenUp() = 0;
    virtual void PenDown() = 0;
    virtual void Forward(int distance) = 0;
    virtual void Circle(int radius) = 0;
    virtual void Turn(int degrees) = 0;
    virtual void GoTo(int x, int y) = 0;
    virtual void Head(int angle) = 0;
    virtual int GetX() const = 0;
    virtual int GetY() const = 0;
};

為Turtle介面編寫mock類的步驟如下:

從Turtle派生一個類MockTurtle;
選擇想要mock的虛擬函式;
在子類的public:部分對每個要mock的函式編寫一個MOCK_METHOD()宏;
將函式簽名複製貼上到宏引數中,並分別在返回型別和函式名之間以及函式名和參數列之間新增一個逗號;
對於const成員函式,新增第4個宏引數(const);
覆蓋虛擬函式建議新增override關鍵字:對於非const成員函式,第4個宏引數為(override);對於const成員函式,第4個宏引數為(const, override)。

#include <gmock/gmock.h>

#include "turtle.h"

class MockTurtle : public Turtle {
public:
    ~MockTurtle() override = default;

    MOCK_METHOD(void, PenUp, (), (override));
    MOCK_METHOD(void, PenDown, (), (override));
    MOCK_METHOD(void, Forward, (int distance), (override));
    MOCK_METHOD(void, Circle, (int radius), (override));
    MOCK_METHOD(void, Turn, (int degrees), (override));
    MOCK_METHOD(void, GoTo, (int x, int y), (override));
    MOCK_METHOD(void, Head, (int angle), (override));
    MOCK_METHOD(int, GetX, (), (const, override));
    MOCK_METHOD(int, GetY, (), (const, override));
};

在測試中使用mock類

在測試中使用mock類的典型方式如下:

建立mock物件;
指定期望的呼叫方式(什麼方法、以什麼引數、被呼叫幾次等),同時也可以指定方法被呼叫時的行為;
在被測函式中使用mock物件,同時也可以使用斷言檢查函式結果;
當mock物件被銷燬時(測試函式返回前),gMock會自動檢查期望的呼叫是否滿足,如果不滿足測試將會失敗。
painter.h

#include "turtle.h"

class Painter {
public:
    explicit Painter(Turtle* turtle): turtle_(turtle) {}
    bool DrawLine(int x1, int y1, int x2, int y2);
    bool DrawRectangle(int x, int y, int length, int width);
    bool DrawCircle(int x, int y, int r);
private:
    Turtle* turtle_;
};

painter.cc

#include "painter.h"

bool Painter::DrawLine(int x1, int y1, int x2, int y2) {
    turtle_->GoTo(x1, y1);
    turtle_->PenDown();
    turtle_->GoTo(x2, y2);
    turtle_->PenUp();
    return true;
}

bool Painter::DrawRectangle(int x, int y, int length, int width) {
    if (length <= 0 || width <= 0)
        return false;
    turtle_->GoTo(x, y);
    turtle_->Head(270);
    turtle_->PenDown();
    turtle_->Forward(width);
    turtle_->Turn(90);
    turtle_->Forward(length);
    turtle_->Turn(90);
    turtle_->Forward(width);
    turtle_->Turn(90);
    turtle_->Forward(length);
    turtle_->PenUp();
    return true;
}

bool Painter::DrawCircle(int x, int y, int r) {
    if (r <= 0)
        return false;
    turtle_->GoTo(x, y - r);
    turtle_->Head(0);
    turtle_->PenDown();
    turtle_->Circle(r);
    turtle_->PenUp();
    return true;
}

painter_test.cc

#include <gmock/gmock.h>

#include "painter.h"
#include "mock_turtle.h"

using ::testing::AtLeast;

TEST(PainterTest, DrawCircle) {
    MockTurtle turtle;
    EXPECT_CALL(turtle, PenDown()).Times(AtLeast(1));

    Painter painter(&turtle);
    EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
}

該測試驗證mock物件turtle的PenDown()方法將被呼叫至少一次,如果該方法最終沒有被呼叫,則測試失敗。

設定期望

gMock使用EXPECT_CALL()宏來對mock函式設定期望,通用語法為:

EXPECT_CALL(mock_object, method(matchers))
    .Times(cardinality)
    .WillOnce(action)
    .WillRepeatedly(action)

;
Times()、WillOnce()和WillRepeatedly()這三個子句必須按順序寫。

匹配器:驗證引數

匹配器(matcher)用於驗證一個引數,可用於EXPECT_THAT()或EXPECT_CALL()斷言。對於EXPECT_CALL(),匹配器由第二個引數中的參數列指定,用於驗證mock函式的實際引數。

直接指定引數值表示期望引數等於指定的值:

// 期望turtle.Forward(100)被呼叫
EXPECT_CALL(turtle, Forward(100));

這裡的100等價於Eq(100)。

萬用字元_表示任意值:

using ::testing::_;
// 期望turtle.GoTo(50, y)被呼叫,y為任意值
EXPECT_CALL(turtle, GoTo(50, _));

如果不對引數做任何限制,則可以省略參數列:

// 期望turtle.Forward(x)被呼叫,x為任意值
EXPECT_CALL(turtle, Forward);
// 期望turtle.GoTo(x, y)被呼叫,x和y為任意值
EXPECT_CALL(turtle, GoTo);

基數:驗證呼叫次數

基數(cardinality)用於驗證mock函式的呼叫次數,由EXPECT_CALL()後面跟著的Times()子句指定,至多使用一次。
直接指定數字表示恰好呼叫指定的次數:

// 期望turtle.Forward(100)被呼叫恰好兩次
EXPECT_CALL(turtle, Forward(100)).Times(2);

AtLeast(n)表示至少被呼叫n次:

using ::testing::AtLeast; 
// 期望turtle.PenDown()被呼叫至少一次
EXPECT_CALL(turtle, PenDown()).Times(AtLeast(1));

如果沒有指定Times()子句,則gMock會按以下規則推斷基數:

如果沒有WillOnce()和WillRepeatedly(),則基數為Times(1)
如果有n個WillOnce()、沒有WillRepeatedly(),其中n≥1,則基數為Times(n)
如果有n個WillOnce()、一個WillRepeatedly(),其中n≥0,則基數為Times(AtLeast(n))

動作:被呼叫時的行為

動作(action)用於指定mock函式被呼叫時的行為,也叫做打樁(stubbing)。

如果沒有指定,則mock函式被呼叫時會執行預設動作:

void函式直接返回
返回型別是內建型別的函式返回對應型別的預設值:bool型別為false,數值型別為0,指標型別為nullptr
返回型別有預設建構函式的則返回預設構造的值
可以使用一個或多個WillOnce()子句以及一個可選的WillRepeatedly()子句指定動作。例如:

using ::testing::Return;
EXPECT_CALL(turtle, GetX())
    .WillOnce(Return(100))
    .WillOnce(Return(200))
    .WillOnce(Return(300));

表示turtle.GetX()將被呼叫恰好三次分別返回100、200、300。

EXPECT_CALL(turtle, GetY())
    .WillOnce(Return(100))
    .WillOnce(Return(200))
    .WillRepeatedly(Return(300));

表示turtle.GetY()將被呼叫至少兩次,前兩次分別返回100和200,之後返回300。

原文https://blog.csdn.net/zzy979481894/article/details/127177663?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171409665116800226554635%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171409665116800226554635&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-1-127177663-null-null.142v100pc_search_result_base6&utm_term=google%20test&spm=1018.2226.3001.4187