單元測試的核心挑戰:被測函數內部依賴外部資源(資料庫、網路服務、硬體介面等)。若不隔離依賴,測試將變得緩慢、不穩定、難以構造邊界條件。
典型場景:
class DataGetter {
Client* m_ptr_client;
public:
bool Run() {
std::string data;
bool ret = m_ptr_client->GetData(data); // 依賴 TCP 協議從服務端獲取數據
// ... 處理 data ...
return ret;
}
};
直接測試 Run() 需啟動真實服務端、構造網路環境,極不現實。
利用 C++ 多態機制:將依賴抽象為介面(虛函數),運行時注入測試專用的「樁」實現,替換真實依賴。
virtual(或抽象介面類)。// 真實依賴介面
class Client {
public:
virtual ~Client() = default;
virtual bool GetData(std::string& data) = 0;
};
// 手寫樁類
class StubClient : public Client {
std::string presetData_;
bool presetResult_;
public:
StubClient(std::string data, bool result) : presetData_(std::move(data)), presetResult_(result) {}
bool GetData(std::string& data) override {
data = presetData_;
return presetResult_;
}
};
// 測試代碼
TEST(DataGetterTest, Run_ReturnsTrue_WhenDataValid) {
DataGetter getter;
getter.m_ptr_client = new StubClient("valid_data", true); // 注入樁
EXPECT_TRUE(getter.Run());
delete getter.m_ptr_client;
}
Google Mock (gMock) 是業界標準 C++ Mock 框架,自動生成 Mock 類,支援期望值設定、參數匹配、動作序列等高級功能。
#include "client.h"
#include <gmock/gmock.h>
class MockClient : public Client {
public:
MOCK_METHOD(bool, GetData, (std::string&), (override));
};
TEST(DataGetterTest, Run_HandlesMultipleCalls) {
auto mockClient = std::make_unique<MockClient>();
DataGetter getter;
getter.m_ptr_client = mockClient.get(); // 注入 Mock
// 設定期望:呼叫 2 次,第 1 次返回成功與數據,第 2 次返回失敗
EXPECT_CALL(*mockClient, GetData(testing::_))
.Times(2)
.WillOnce(testing::DoAll(
testing::SetArgReferee<0>("first_response"),
testing::Return(true)))
.WillOnce(testing::DoAll(
testing::SetArgReferee<0>("second_response"),
testing::Return(false)));
EXPECT_TRUE(getter.Run()); // 第 1 次呼叫
EXPECT_FALSE(getter.Run()); // 第 2 次呼叫
}
| 分類 | 常用語法 | 說明 |
|---|---|---|
| 參數匹配 | _ | 匹配任意值 |
Eq(value) / StrEq("str") | 精確匹配 | |
Ge(10), Lt(5) | 範圍匹配 | |
Ref(variable) | 匹配引用參數 | |
| 返回動作 | Return(value) | 返回固定值 |
SetArgReferee<N>(value) | 設定第 N 個引用參數值 | |
Invoke(callable) | 呼叫自定義函數/lambda | |
Throw(exception) | 拋出異常 | |
| 調用次數 | Times(n) | 確切 n 次 |
AtLeast(n) / AtMost(n) | 至少/至多 n 次 | |
AnyNumber() | 任意次數 |
MethodName_Scenario_ExpectedResult,如 GetData_NetworkTimeout_ReturnsFalse。