【C/C++】單元測試實戰:Stub與Mock框架解析

C++ 單元測試中的 Stub/Mock 框架詳解

在單元測試中,Stub(打樁)Mock都是替代真實依賴以簡化測試的技術。通常,Stub(或 Fake)提供了一個簡化實現,用于替代生產代碼中的真實對象(例如用內存文件系統替代磁盤文件系統),而Mock則是在運行時預設了期望行為的對象,用來驗證代碼與依賴之間的交互是否符合預期。下面我們重點介紹幾種常見的 C++ Stub/Mock 框架:Google Mock、FakeIt、Fake Function Framework (FFF) 和 MockCPP(Mock++),并分析它們的特點、使用示例和適用場景。

Google Mock (gMock)

簡介:Google Mock(簡稱 gMock)是 Google Test 庫提供的 C++ Mock 框架,用于創建模擬類和方法。它允許使用宏 MOCK_METHOD 來描述要模擬的接口或虛函數,自動生成 mock 類的實現,用戶可以通過 EXPECT_CALL 等語法直觀地設置預期調用和行為。gMock 功能強大,提供豐富的參數匹配器、調用次數控制和動作指定(如 .WillOnce(Return(x)))等特性,非常適合對面向對象的接口進行精細控制。

安裝與集成:gMock 與 Google Test 一起發布。Google Test 源碼倉庫中就包含了 gMock,可以使用 CMake 或 Bazel 構建整個 googletest 包。也可以通過包管理器(如 vcpkg、conan 等)安裝 googletest 包,它們會自動包含 gMock。測試代碼中通常需要包含 <gtest/gtest.h><gmock/gmock.h> 頭文件。由于 gMock 與 Google Test 深度集成,使用起來非常方便。

樣例代碼:下面是一個簡單的示例:假設有一個 Calculator 接口,我們為其創建一個 mock 并測試調用行為。

// 被測接口
class Calculator {
public:virtual ~Calculator() {}virtual int add(int a, int b) = 0;
};// 創建 Mock 類,繼承接口并使用 MOCK_METHOD 宏
class MockCalculator : public Calculator {
public:MOCK_METHOD(int, add, (int a, int b), (override));
};TEST(CalculatorTest, AddTest) {MockCalculator mockCalc;// 設置預期:調用 add(2,3) 返回 5 且調用一次EXPECT_CALL(mockCalc, add(2, 3)).Times(1).WillOnce(Return(5));// 使用 mock 對象int result = mockCalc.add(2, 3);ASSERT_EQ(result, 5);
}

以上示例中,MOCK_METHOD 宏在 MockCalculator 類中自動生成了 add 方法的模擬實現;EXPECT_CALL 用于聲明對 add 的期望調用(參數和返回值)。當測試執行時,如果模擬對象的使用與期望不符,gMock 會立即報告錯誤。

優缺點分析

  • 優點:功能強大,特別適合模擬 C++ 類的虛函數/接口,可以細粒度控制方法調用的參數、順序和返回值。提供了豐富的匹配器(如 _, HasSubstr, 自定義 lambda 等)和動作(WillOnceWillRepeatedlyDoAll 等),以及驗證調用次數的機制。gMock 與 Google Test 緊密集成,文檔和社區資源豐富。典型的測試流程符合“Arrange-Act-Assert”模式。
  • 缺點:只能模擬類的虛函數或接口方法,對普通的自由函數、全局函數或靜態函數支持不好。若被測代碼中不使用接口/虛函數,需要重構設計才能使用 gMock。gMock 大量使用宏實現,學習曲線較陡峭,編譯速度可能較慢,錯誤信息有時不夠直觀。

適用場景:gMock 非常適合純 C++ 面向對象的場景,如通過接口或虛類進行模塊間交互時的測試。比如在測試依賴于數據庫、網絡等資源的類時,可以定義相應的接口并使用 gMock 來模擬依賴,從而專注于驗證業務邏輯。在 C 語言風格或函數指針場景下,通常需要先包裝或抽象出接口,gMock 才能使用。

與測試框架結合:gMock 與 Google Test 一起使用。測試代碼中可以使用 Google Test 的 TESTTEST_F 來定義測試用例,斷言宏(ASSERT_*/EXPECT_*)來自 Google Test。中的流程步驟描述了典型的使用方式:導入 gMock 名稱、創建 mock 對象、設置期望、調用被測代碼,然后自動校驗所有期望。由于 gMock 本身就是 Google Test 的一部分,只要正確引入頭文件即可在任何 Google Test 環境下使用。

FakeIt 框架

簡介:FakeIt 是一個輕量級的 C++ mocking 框架,基于 C++11 實現,支持 GCC、Clang、MSVC 等主流編譯器。它采用頭文件級別實現(header-only),提供了簡潔而富有表現力的 API:使用 Mock<T> 創建 mock 對象,使用 When(Method(mock, func)).Return(val) 指定返回值,使用 Verify(Method(mock, func)) 驗證調用情況。FakeIt 的設計目標是讓 C++ mocking 更加易用,用最少的樣板代碼就能實現常見的 mock 用例。

安裝與集成:FakeIt 僅需包含頭文件即可使用,無需單獨編譯庫。可以從 FakeIt GitHub 獲取源碼,或者直接使用單頭文件版本(fakeit.hppfakeit_single_header.hpp)。框架預先配置了與 Google Test、Boost.Test、Catch2 等流行測試框架的集成方案,使得測試斷言輸出可以與這些框架統一。如果項目沒有使用主流框架,也可以使用 FakeIt 的 standalone 模式(自帶簡單斷言機制)。

樣例代碼:下面演示了 FakeIt 的基本用法,測試一個接口 IFoo 的模擬行為。

struct IFoo {virtual ~IFoo() {}virtual int foo(int x) = 0;
};TEST(FakeItTest, Basic) {using namespace fakeit;// 創建 IFoo 的 mock 對象Mock<IFoo> mockFoo;// 設置 foo 方法被調用時返回 42When(Method(mockFoo, foo)).Return(42);// 從 mock 對象獲取接口引用IFoo &iFoo = mockFoo.get();int result = iFoo.foo(10);ASSERT_EQ(result, 42);// 驗證 foo 是否被調用過一次Verify(Method(mockFoo, foo));
}

上述代碼中,Mock<IFoo> mockFoo 創建了一個 IFoo 的模擬實例;When(Method(mockFoo, foo)).Return(42) 指定當調用 foo 時返回 42;mockFoo.get() 則獲取該 mock 的引用用于測試調用。最后 Verify(Method(mockFoo, foo)) 用于驗證 foo 至少被調用過一次。FakeIt 的語法直觀,通常只需一兩行代碼就能完成設置和驗證。

優缺點分析

  • 優點:API 簡潔、使用成本低。作為頭文件庫,無需額外安裝步驟,易于集成到任意項目。支持所有主流編譯器和 C++11/14/17 標準;框架提供對現有對象的“間諜(Spy)”功能,可在不修改生產代碼的情況下監控調用。FakeIt 預先配置了對 Google Test、Boost.Test 等常見框架的兼容性,測試報告樣式統一。其 Arrange-Act-Assert 風格清晰,使測試代碼可讀性高。
  • 缺點:功能上相對 gMock 稍遜一籌,例如對復雜場景(調用序列、回調等)的支持有限。在較早版本中,錯誤信息和調試支持也不如 gMock 豐富。由于社區較小,可參考的中文資料有限,學習資源不如 Google 官方那么多。但對于一般的函數調用場景,FakeIt 已經足夠簡便強大。

適用場景:FakeIt 適用于需要快速創建 mock 并且代碼已經使用接口或虛函數設計的 C++ 項目。它可以方便地模擬依賴接口,專注于測試邏輯而不啟動實際組件。例如,在測試與數據庫交互的類時,可以用 FakeIt 模擬數據庫接口,控制返回數據,驗證業務邏輯如何處理這些數據。FakeIt 也支持對已有實例“監控”,適合在不修改生產代碼前提下捕獲方法調用并驗證行為。

與測試框架結合:FakeIt 與 Google Test 等框架可以很好地配合。例如,上述示例使用了 Google Test 的 TEST 和斷言宏。FakeIt 內部提供了與這些框架的集成配置(如 #include "fakeit.hpp" 會自動鏈接對應的測試框架斷言機制),使得 FakeIt 的錯誤信息可以直接作為 Google Test 的失敗報告。除了 Google Test,FakeIt 也支持 Catch2、Boost.Test 等,只需在包含頭文件時使用不同配置即可。

Fake Function Framework (FFF)

簡介:Fake Function Framework(簡稱 FFF)是一個面向 C 語言的微型假函數框架,用于創建 C 函數級別的測試替身。它非常輕量,完全用宏實現,適合對純 C 代碼(特別是嵌入式系統中的 C 接口)進行單元測試打樁。FFF 可以自動記錄函數調用次數和參數值,而不需要編寫手動的 fake 函數。它的設計初衷是“讓測試假函數的創建不再乏味”。

安裝與集成:FFF 也是頭文件實現,只需下載 fff.h 并在測試代碼中包含即可使用。通常將 fff.h 文件置于測試項目中,寫 #include "fff.h" 后通過定義宏開啟功能,比如 DEFINE_FFF_GLOBALS;。然后可以使用 FFF 提供的宏來定義假函數。FFF 不依賴特定的測試框架,常與 Ceedling/Unity(C 語言測試工具)一起使用,但也可以在 C++ 項目中通過 extern "C" 包裝后與 Google Test 等框架結合。

樣例代碼:假設有一個 C 語言函數 void hardware_init() 需要在測試中模擬:

// 在 test.c(pp) 中:
#include "fff.h"
DEFINE_FFF_GLOBALS;
FAKE_VOID_FUNC(hardware_init);TEST(CFunctionTest, InitTest) {// 調用被測代碼(假設其中會調用 hardware_init())system_init();// 驗證 hardware_init() 被調用了一次ASSERT_EQ(hardware_init_fake.call_count, 1);
}

上例中,FAKE_VOID_FUNC(hardware_init) 宏定義了一個名為 hardware_init 的假函數,并生成對應的控制結構體(hardware_init_fake)。當調用 system_init() 時,如果它內部調用了 hardware_init(),那么 hardware_init_fake.call_count 將被增加。測試最后通過斷言檢查調用次數。對有返回值的函數,可以使用 FAKE_VALUE_FUNC(retType, func, args...);對帶參數的函數,宏會自動生成相應的參數記錄(例如 foo_fake.arg0_val 等)。

優缺點分析

  • 優點:極為輕量和簡單,特別適合 C 語言環境的測試。不需要復雜的構建,直接包含頭文件即可使用。對于純 C 函數(如硬件驅動接口),可以快速生成假函數并自動統計調用次數和參數,極大減少手寫 stub 的工作量。依賴少,適用于嵌入式系統等對資源敏感的場景。
  • 缺點:功能有限,僅能處理全局或靜態 C 函數的調用情況,無法模擬 C++ 類的成員函數。它不提供像 gMock 那樣的復雜行為控制或斷言功能,只能記錄調用次數和參數值,需手動編寫斷言或返回邏輯。對于面向對象的設計場景,FFF 無法直接使用;此時需要將依賴包裝成 C 接口才能用 FFF 打樁。

適用場景:FFF 主要用于 C 風格的代碼測試,特別是嵌入式軟件的硬件抽象接口測試。它適用于替代庫函數、驅動函數等,例如模擬 I/O、時鐘、中斷等函數調用。對于 C++ 類、虛函數和接口,FFF 不適用。通常在需要 Stub 出單個函數行為時使用 FFF,而不用于復雜的 Mock 行為驗證。

與測試框架結合:FFF 本身只負責假函數的生成,不包含斷言框架。因此,測試時可以選擇任意 C/C++ 測試框架來執行斷言。例如,可以在 Google Test 的 TEST 中包含 fff.h 并使用 ASSERT_EQ 來檢查 foo_fake.call_count。在 C 項目中,也常與 Ceedling/Unity 配合使用(Unity 對 foo_fake.call_count 做斷言),從而實現測試打樁。不過需要注意 FFF 的宏通常定義為 C 風格,全局變量名可能會與測試框架中的符號沖突,需要妥善管理。

MockCPP (Mock++)

簡介:MockCPP(又稱 Mock++)是一個跨平臺的 C++ Mock 框架,受 jMock 等 Java 框架啟發設計。它使用 C++11 的可變參數模板和宏來實現 Mock 功能,目標是在不依賴編譯器內部機制的情況下提供簡單的 Mock 支持。MockCPP 的核心思想是:對虛函數進行模擬,需要通過繼承和宏來聲明要模擬的函數,然后在測試中設置行為和驗證調用。

安裝與集成:MockCPP 可從其 GitHub 倉庫 克隆獲取,編譯時需要支持 C++11。它不像 gMock 那樣集成于常用測試框架,而是一個獨立的庫,編譯后鏈接到測試項目。使用時,在測試代碼中包含 MockCPP 的頭文件,如 #include <mockpp/mockpp.h> 以及需要的頭。MockCPP 本身不提供斷言功能,因此通常與 Google Test、Boost.Test 等一起使用。只要在測試用例中創建了 Mock 對象并設置期望,就可以使用測試框架的斷言來檢查驗證結果。

樣例代碼:下面示例演示了使用 MockCPP 模擬一個 IWidget 接口及其方法調用:

class IWidget {
public:virtual ~IWidget() {}virtual bool isReady() = 0;
};class MockWidget : public IWidget, public mockpp::Mock<IWidget> {
public:MOCK_FUNCTION(bool, isReady);
};TEST(MockCPPTest, Basic) {MockWidget widget;using namespace mockpp;// 設置調用 isReady 時返回 truePROBE(&widget, isReady).toReturn(true);// 調用 mock 方法bool r = widget.isReady();ASSERT_TRUE(r);// 驗證 isReady 被調用ASSERT_TRUE(VALIDATE(&widget, isReady).called());
}

示例中,MOCK_FUNCTION(bool, isReady); 宏在 MockWidget 類中聲明了要模擬的 isReady 方法。在測試中使用 PROBE(&widget, isReady).toReturn(true) 指定調用時返回值為 true。最后使用 VALIDATE(&widget, isReady).called() 來驗證該方法確實被調用過。MockCPP 的 PROBEVALIDATE 類似于 gMock 的 EXPECT_CALL 和驗證機制,但表達方式不同。

優缺點分析

  • 優點:MockCPP 實現簡潔,完全基于標準 C++11,不依賴于非標準特性(如內部 vtable),具有良好的可移植性。通過宏聲明和模板實現,編譯器兼容性強。框架本身較輕量,在某些需要對編譯器支持有嚴格要求的項目中可能更受青睞。MockCPP 提供了類似 gMock 的調用順序驗證(默認嚴格順序),也支持放松驗證(RelaxedMock 模板)等高級功能。
  • 缺點:使用的人較少,文檔和社區支持有限。需要手動繼承接口并編寫 mock 類,使用門檻較高。與 gMock/FakeIt 相比,缺少現成的匹配器和動作鏈(雖然可以用 C++11 lambda 自行實現類似功能)。MockCPP 主要針對基于類的接口場景,對純 C 函數或數據驅動的場景不適用。

適用場景:MockCPP 適用于需要模擬 C++ 類和接口的場景,與 gMock/FakeIt 作用相似。例如在需要嚴格控制調用順序的測試中,可以利用 MockCPP 默認的嚴格驗證機制。由于其標準化實現,也可用于對可移植性要求高的項目。對于 C 風格的代碼或函數指針場景,MockCPP 不適用,此時應選擇 FFF 等其他工具。

與測試框架結合:MockCPP 與常見 C++ 單元測試框架配合使用。例如示例中我們使用了 Google Test 的 TEST 和斷言。MockCPP 不包含自己的斷言機制,通常在測試中使用 Google Test 的 ASSERT_TRUEASSERT_EQ 等來判斷 VALIDATE() 的結果。也可以在 Boost.Test 或其他框架中同樣使用,只要在測試結束時檢驗 mock 對象的調用情況即可。

Trompeloeil 框架

Trompeloeil 是一個**面向現代 C++(C++14 及以上)**的靜態類型(statically typed)模擬框架。與許多舊式模擬框架不同,它利用 RAII 機制來管理期望(expectation)的生命周期,并提供類似自然語言的表達方式來描述動作、過濾器、副作用和返回值等。該框架采用頭文件單一依賴(header-only),無需額外鏈接庫,并采用 Boost Software License 1.0 開源授權。

  • 安裝集成:Trompeloeil 以頭文件方式分發。可以通過 GitHub 倉庫 克隆后直接將 trompeloeil.hpp 包含到測試項目中,也可通過包管理工具(如 Conan)引入最新版。只需確保編譯器支持 C++14 即可(GCC 4.9、Clang 3.5+ 等),無需其他依賴。

  • 核心用法示例:Trompeloeil 的基本使用方式與 Google Mock 類似:首先定義一個繼承自要模擬接口的 Mock 類,并使用宏 MAKE_MOCKnMAKE_CONST_MOCKn 來創建模擬函數。例如,假設接口 Warehouse 有兩個虛函數 hasInventoryremove,可寫:

    class WarehouseMock : public Warehouse {
    public:MAKE_CONST_MOCK2(hasInventory, bool(const std::string&, size_t));MAKE_MOCK2(remove, void(const std::string&, size_t));
    };
    

    然后在測試中設置期望和返回值,用 REQUIRE_CALL 宏:

    TEST_CASE("Order is filled when inventory is sufficient", "[Trompeloeil]") {Order order("Whisky", 50);WarehouseMock warehouse;REQUIRE_CALL(warehouse, hasInventory("Whisky", 50)).RETURN(true);REQUIRE_CALL(warehouse, remove("Whisky", 50));order.fill(warehouse);REQUIRE(order.is_filled());
    }
    

    上述代碼中,REQUIRE_CALL 表示被測代碼必須在測試作用域結束前調用指定的模擬方法,否則測試失敗。Trompeloeil 還支持順序驗證(trompeloeil::sequence)、通配符 _、拋出異常、替代函數實現等高級功能。

  • 優缺點

    • 優點:Trompeloeil 支持強類型(編譯時)檢查,語法直觀、靈活,且通過 RAII 確保在析構時驗證所有期望。它無需鏈接額外庫(頭文件實現),升級方便,并持續活躍維護(最近版本支持協程、正則匹配等新特性)。
    • 缺點:僅支持模擬虛函數(接口方法),無法直接模擬普通全局函數或非虛成員函數(需要通過間接依賴注入來變通)。此外,要求項目使用 C++14 以上標準,老舊編譯器下語法可能受限。由于高度靈活,有時錯誤信息對新手可能不夠直觀。
  • 適用場景:Trompeloeil 非常適合模擬面向接口編程的 C++ 代碼(使用純虛基類或抽象類)。它在處理復雜的對象交互和高級匹配條件(如多重參數、序列要求)時非常方便。但對于 C 樣式函數、函數指針或全局函數等非類成員函數,則需要其他框架(如 HippoMocks、FFF)來補充支持。

HippoMocks 框架

HippoMocks(常稱 Hippo Mocks)是由 Wouter van Ooijen 編寫的一個單頭文件C++模擬框架,面向 C++11 及以上的環境。它最初是作者個人使用的輕量庫,現在開源發布,并且無需任何第三方依賴。HippoMocks 的主要特點是支持多種函數類型的模擬,包括類成員函數、全局函數、靜態函數等(但靜態函數必須可被鏈接hook),這在 C++ 模擬庫中較為少見。

  • 安裝集成:使用 HippoMocks 非常簡單。只需從GitHub 倉庫下載單個頭文件 hippomocks.h,然后在測試代碼中包含它即可。它與常見的測試框架(如 CppUnit、Google Test)兼容,通過 MockRepository 對象來管理模擬實例。

  • 核心用法示例:HippoMocks 通過 MockRepository 創建模擬對象,并使用 ExpectCall 等方法設定期望。例如,要模擬如下接口:

    struct IBar {virtual void b() = 0;virtual int c(const std::string&) = 0;virtual ~IBar() = default;
    };
    

    在測試中可以這樣寫:

    TEST_CASE("HippoMocks example", "[Hippo]") {MockRepository mocks;// 創建接口 IBar 的模擬IBar* barMock = mocks.Mock<IBar>();// 設定期望:方法 b() 必須被調用mocks.ExpectCall(barMock, IBar::b);// 設定期望:方法 c("hello") 返回 42mocks.ExpectCall(barMock, IBar::c).With("hello").Return(42);// 被測代碼調用 barMock...REQUIRE(barMock->c("hello") == 42);// 作用域結束時 MockRepository 析構,會檢查所有期望是否滿足:contentReference[oaicite:16]{index=16}。
    }
    

    上述代碼中,ExpectCall(barMock, IBar::c).With("hello").Return(42) 直接說明:調用 barMock->c("hello") 時返回 42。HippoMocks 還支持 Throw() 拋異常、OnCall()(可選調用)、Do() 執行自定義替代函數等高級功能。與其他庫相比,HippoMocks 的語法也接近自然語言,代碼可讀性較好。

  • 優缺點

    • 優點:HippoMocks 最大的優勢是無需依賴(僅包含頭文件)且使用簡單。它支持 Windows、Linux、Mac 等主流平臺(MSVC、GCC、Clang),并且可以模擬靜態/全局函數,這是多數 C++ Mock 框架無法做到的(需要函數在編譯時未被內聯)。此外,由于框架年頭較久,社區示例和教程較多(例如 Assembla wiki 等),學習成本低。
    • 缺點:HippoMocks 的文檔相對稀缺(官方未提供詳盡參考),出錯時的錯誤信息不夠友好,調試難度較大。由于采用底層方法(動態鏈接鉤子)來實現靜態函數模擬,其機制可能不被所有平臺完全支持,而且對內聯函數無能為力。此外,HippoMocks 的更新頻率已經很低(最新發布在 2016 年左右),項目維護不如 Trompeloeil 和 FakeIt 活躍。
  • 適用場景:HippoMocks 非常適合對已有 C 代碼或混合 C/C++ 項目進行單元測試時使用,尤其是需要模擬非類成員函數時(例如模擬系統調用、庫函數)。對于接口式設計的 C++ 類,HippoMocks 也能輕松應對。但它不屬于靜態類型框架,對模板或重載可能需要額外處理(可以使用 ExpectCallOverload 顯式指定函數簽名)。當需要集成到現有的 C++ 測試框架時,只需包含頭文件即可,無需鏈接額外庫。

Catch2 自帶的 Mock/Fake 能力

Catch2 本身是一個流行的 C++ 單元測試框架(支持頭文件形式,無需鏈接)。需要明確的是,Catch2 并不提供內置的完整模擬功能。JetBrains 文檔也指出:“與 Boost.Test 類似,Catch2 不提供模擬功能。但可以將其與獨立的模擬框架(如 HippoMocks、FakeIt、Trompeloeil 等)結合使用”。換言之,Catch2 傾向于專注測試運行和斷言邏輯,而將 Mock 交給第三方庫來完成。

  • 內建 Fake 功能:雖然 Catch2 沒有專門的模擬模塊,但在 Catch2 v3 中提供了一些輔助支持。例如,Catch2 的 “extras” 中包含了用于與 Trompeloeil 等框架配合使用的頭文件(如 <catch2/trompeloeil.hpp>),方便在 Catch2 測試文件中使用 Trompeloeil 宏。如果選擇使用 FakeIt,還可以在 CMake 中配置 FakeIt 的 catch 預定義版本,使其自動集成 Catch2 的斷言機制。總的來說,Catch2 自身沒有專用的 REQUIRE_CALLEXPECT_CALL 等接口,但可以通過引入其他庫來實現類似功能。

  • 示例:假設借助 FakeIt 在 Catch2 測試中使用模擬對象,可以按 FakeIt 文檔設置配置:

    // 假設已經在 CMake 中引入了 Catch2 和 FakeIt
    #include "fakeit.hpp"
    #include <catch2/catch_test_macros.hpp>
    using namespace fakeit;struct SomeInterface {virtual int foo(int) = 0;virtual ~SomeInterface() = default;
    };TEST_CASE("Catch2 + FakeIt example", "[FakeIt]") {Mock<SomeInterface> mock;When(Method(mock, foo).Using(5)).Return(10);SomeInterface& i = mock.get();REQUIRE(i.foo(5) == 10);Verify(Method(mock, foo).Using(5)).Exactly(1);
    }
    

    如上,Catch2 測試框架負責執行斷言 (REQUIRE),而 FakeIt 負責定義模擬邏輯。FakeIt 提供預配置的 catch 配置,能無縫打印斷言失敗信息。

  • 優缺點

    • 優點:如果項目已經使用 Catch2 進行單元測試,集成其他 Mock 框架相對容易。例如上述示例無需鏈接額外庫,只需要包含適當頭文件。Catch2 支持多種斷言類型和 BDD 風格,可用于測試生成數據、參數化測試等場景,其測試執行速度較快。對于簡單需求,可以通過 Catch2 的 GENERATESECTION 等功能模擬不同的輸入情形,算是一種“輕量級”的偽模擬用法。
    • 缺點:因為沒有內置專門的 Mock 接口,實現模擬需要額外引入第三方庫。使用 Catch2 配合 Mock 框架時,需要考慮集成配置(如鏈接 FakeIt 的 Catch 集成頭)。Catch2 也不直接支持模擬非虛函數或全局函數,這些問題仍需依賴 HippoMocks、FFF 等專門工具來解決。總結來說,Catch2 作為測試框架本身不作為模擬框架使用,它的“Fake”功能實際上是對其他庫的依賴或利用基本測試功能模擬輸入。

框架對比與適用場景

下表對比了 Trompeloeil、HippoMocks、Catch2(配合 FakeIt)、以及常見的其它框架(gMock、FakeIt、FFF、MockCPP)在功能特性方面的差異:

框架模擬目標C++要求實現方式許可維護狀態備注
TrompeloeilC++ 接口類(純虛函數)C++14+頭文件(BSL-1.0)BSL-1.0活躍(近年頻繁更新)支持序列、正則等高級匹配;無法直接模擬全局/靜態函數
HippoMocks接口類 + 靜態/全局函數C++11+頭文件(LGPL)LGPLv2.1舊(近年幾乎未更新)支持 Hook 模擬 C 樣式函數;簡單易用但文檔少
Catch2 + FakeIt接口類Catch2 C++11+ / FakeIt C++11+鏈接 Catch2 與 FakeItCatch2: BSL-1.0
FakeIt: MIT
活躍Catch2 本身無模擬功能;FakeIt 支持所有主流編譯器,無參數個數限制
gMock(GoogleMock)接口類C++11+鏈接 GoogleTest (BSD)BSD-3活躍功能強大豐富、社區廣泛;語法較古老,錯誤信息冗長;不支持靜態函數模擬
FakeIt接口類C++11+頭文件 (MIT)MIT活躍單頭文件,易用,支持 Spy、動態轉換、任意參數個數;無需手動實現返回值函數
FFFC 全局函數C89/C99/C++ (C 風格)頭文件 (BSD)BSD-like活躍極簡框架,只針對 C 樣式的全局/靜態函數;不支持 C++ 類。
MockCPP(Mock++)接口類C++11+頭文件 (LGPL)LGPL類似 Java 的 JMock,支持虛擬函數,模板較少;性能較慢,缺少新特性。

說明:表中“模擬目標”指框架設計時主要支持的被模擬對象類型。比如 Trompeloeil、gMock、FakeIt、MockCPP 均以 C++ 接口類(純虛函數)為目標;HippoMocks 和 FFF 則能模擬普通函數/靜態函數。C++ 版本要求和許可證摘自官方說明。Catch2 本身不限制 C++ 標準,但與 FakeIt 結合時需要至少 C++11。維護狀態一欄基于最近代碼提交和發行情況概括。

從上述對比可以看出:

  • Trompeloeil 適合進行現代 C++ 編程時的接口模擬,語法與 GoogleMock 類似但更加現代化。
  • HippoMocks 適合需要模擬 C 風格函數或已有非面向接口代碼的場景,因其 Hook 特性可攔截全局/靜態調用。
  • Catch2 自身更偏向作為測試運行器,適合簡單場景或與其他框架結合使用;如果不需要復雜 Mock,也可直接編寫替身函數(Fake 函數)和 GENERATE 配置來模擬不同輸入。
  • gMock/FakeIt 常與 Google Test 或其他測試框架配合使用,是成熟的選擇。FakeIt 作為輕量級單頭文件框架易上手,而 gMock 功能最豐富。
  • FFF 專門用于 C 函數模擬,適用于嵌入式或遺留 C 代碼測試。
  • MockCPP 作為早期框架,現在用途較少,新項目一般使用上述更現代的框架。

小結

綜上,Trompeloeil、HippoMocks 和 Catch2(配合 FakeIt)各有側重。Trompeloeil 提供了表達能力強、類型安全的 C++ 接口 Mock 功能;HippoMocks 則通過輕量級單頭文件方式支持更多函數形式,包括非對象函數。Catch2 本身不帶模擬機制,只負責管理測試生命周期,但可靈活地與任意 Mock 庫集成。在選擇框架時,應根據項目語言標準、代碼風格(面向接口或過程式)、平臺兼容性等因素綜合考慮。通過比較以上框架的特點,可以為不同場景選用合適的模擬工具,提高單元測試的編寫效率和代碼質量。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/86560.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86560.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86560.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

工廠 + 策略設計模式(實戰教程)

在軟件開發中&#xff0c;設計模式是解決特定問題的通用方案&#xff0c;而工廠模式與策略模式的結合使用&#xff0c;能在特定業務場景下發揮強大的威力。本文將基于新增題目&#xff08;題目類型有單選、多選、判斷、解答&#xff09;這一業務場景&#xff0c;詳細闡述如何運…

Nuxt3中使用 Ant-Design-Vue 的BackTop 組件實現自動返回頁面頂部

在現代 Web 應用中&#xff0c;提供一個方便用戶返回頁面頂部的功能是非常重要的。Ant Design Vue 提供了 BackTop 組件&#xff0c;可以輕松實現這一功能。本文將詳細介紹如何在 Nuxt 3 項目中使用 <a-back-top/> 組件&#xff0c;并通過按需引入的方式加載組件及其樣式…

在統信UOS(Linux)中構建SQLite3桌面應用筆記

目錄 1 下載lazarus 2 下載sqlite3源碼編譯生成庫文件 3 新建項目 4 設置并編譯 一次極簡單的測試&#xff0c;記錄一下。 操作系統&#xff1a;統信UOS&#xff0c; 內核&#xff1a;4.19.0-arm64-desktop 處理器&#xff1a;D3000 整個流程難點是生成so庫文件并正確加…

Host ‘db01‘ is not allowed to connect to this MariaDB server 怎么解決?

出現錯誤 ERROR 1130 (HY000): Host db01 is not allowed to connect to this MariaDB server&#xff0c;表示當前用戶 test 沒有足夠的權限從主機 db01 連接到 MariaDB 服務器。以下是逐步解決方案&#xff1a; 1. 檢查用戶權限 登錄 MariaDB 服務器&#xff08;需本地或通過…

打造高可用的大模型推理服務:基于 DeepSeek 的企業級部署實戰

&#x1f4dd;個人主頁&#x1f339;&#xff1a;一ge科研小菜雞-CSDN博客 &#x1f339;&#x1f339;期待您的關注 &#x1f339;&#x1f339; 一、引言&#xff1a;從“能部署”到“可用、好用、能擴展” 近年來&#xff0c;隨著 DeepSeek、Qwen、Yi 等開源大模型的持續發…

Spring Boot 使用 ElasticSearch

第一步&#xff0c;開啟本地的 ElasticSearch 啟動 elasticSearch.bat npm run start (head 插件) 第二步&#xff0c;在 Spring Boot 項目中引入依賴 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-hig…

軟件開發的“中庸之道”:因勢而為,心中有數

【軟件工程】軟件開發的“中庸之道”&#xff1a;因勢而為&#xff0c;心中有數 在軟件開發的方法論討論中&#xff0c;我們常常陷入非此即彼的二元對立&#xff1a;要么追求極致的規范化和流程化&#xff0c;嚴格遵循軟件工程的各項標準&#xff1b;要么完全摒棄方法論&#x…

Go和Elixir極簡HTTP服務對比

Go 和 Elixir 都是我非常喜歡的編程語言&#xff0c;這次來對比下它們實現一個原生極簡 HTTP 服務的過程。 Go 語言標準庫自帶了網絡服務庫&#xff0c;只需要簡單幾行代碼就可以實現一個網絡服務&#xff0c;這也是一開始它吸引我的一個方面。而 Elixir 標準庫本身沒有網絡服…

為何要學習Objective-C?從環境搭建開始

目錄 前言 Swift時代為何還要學Objective-C&#xff1f; 開發環境搭建 1. 安裝Xcode 2. 創建第一個Command Line Tool項目 初識Objective-C代碼 編寫"Hello, Objective-C!" 編譯運行程序 為什么Objective-C中的NSLog和NSString前面都有"NS"前綴&a…

ubuntu18.04安裝 gcc 9以及2019版本tbb

一、安裝gcc 9 ubuntu18.04默認是用的gcc7.5 sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt-get install gcc-9 g-9 下面是配置優先用哪個版本的gcc和g &#xff0c;后面帶的值越大越優先用誰&#xff0c;并且配置完全局生效不僅僅是在當…

JdbcUtils的三個版本以及sql注入問題

JDBC的工具類 1.0版本 JDBC的工具類 2.0版本&#xff08;智能一些&#xff09;&#xff0c;編寫properties屬性文件&#xff0c;程序就可以讀取屬性文件 JDBC的工具類 3.0版本&#xff0c;加入連接池對象 我們封裝jdbc工具類是為了減少代碼重復&#xff0c;方便開發&#xff0…

AS32系列MCU芯片I2C模塊性能解析與調試

國科安芯推出的AS32X601內置的I2C模塊提供了符合工業標準的兩線串行制接口&#xff0c;可用于MCU和外部IIC設備的通訊。IIC總線使用兩條串行線&#xff1a;串行數據線SDA和串行時鐘線SCL。 IIC接口模塊實現了IIC協議的標準模式和快速模式&#xff0c;支持多主機IIC總線架構。其…

釘釘小程序開發實戰:打造一個簡約風格的登錄頁面

在上一篇文章中&#xff0c;我們已經介紹了如何搭建釘釘小程序的基礎環境&#xff0c;并完成了項目的初始化配置。本文將繼續深入&#xff0c;手把手帶你實現一個簡約風格的登錄頁面&#xff0c;這是大多數企業級應用不可或缺的一部分。 釘釘小程序基于前端 Web 技術棧&#x…

論文研讀2-1:多GNSS雙歷元純相位定位-模型建立與誤差分析

后續文章: 論文研讀2-2&#xff1a;多GNSS雙歷元純相位定位-固定模糊度精度增益 論文研讀2-3&#xff1a;多GNSS雙歷元純相位定位-定位精度分析 僅相位定位中的模糊度解算問題 在衛星導航定位中&#xff0c;載波相位測量是實現高精度定位的基礎&#xff0c;但如果僅使用相位測…

Python----OpenCV(圖像増強——圖像平滑、均值濾波、高斯濾波、中值濾波、雙邊濾波)

Python----計算機視覺處理&#xff08;Opencv&#xff1a;圖像噪點消除&#xff1a;濾波算法&#xff0c;噪點消除&#xff09; 一、圖像平滑 圖像平滑處理&#xff08;Smoothing Images&#xff09;&#xff0c;也稱為圖像模糊處理、圖像濾波&#xff08;Images Filtering&am…

筆記:使用EasyExcel導入csv文件出現編碼問題,導致導入數據全為null的解決方法

筆記&#xff1a;使用EasyExcel導入csv文件出現編碼問題&#xff0c;導致導入數據全為null的解決方法 通常情況下&#xff0c;我們使用excel導入&#xff0c;但是部分情況下或者領導要求&#xff0c;我們需要使用csv導入文件&#xff0c;但是csv文件模板下載之后會變成系統當前…

NL2SQL(Natural Language to SQL)優化之道:提升準確率與復雜查詢能力

自然語言 → SQL 的轉譯&#xff08;NL2SQL&#xff09;技術&#xff0c;是讓非技術用戶與數據庫“對話”的橋梁。而在實際應用中&#xff0c;我們不僅需要“能轉”&#xff0c;更要“轉得準、轉得全、轉得快”。 一、什么是 NL2SQL&#xff1f; NL2SQL&#xff08;Natural La…

java中map的循環方式

什么是Map集合&#xff1f; Map是Java中的一個接口&#xff0c;它用于存儲鍵-值對&#xff0c;并且鍵和值都可以是任意對象。它是Java集合框架中的一部分&#xff0c;并提供了一些方法來操作和訪問Map中的元素。 Map中的每個鍵都是唯一的&#xff0c;這意味著不能使用相同的鍵…

python學習筆記(深度學習)

文章目錄 1、概述2、學習內容2.1、pytorch 常見語法2.1.1、sum2.1.2、廣播機制2.1.3、張量2.1.4、DataLoader 2.2、普通語法2.2.1、迭代器 1、概述 本篇博客用來記錄&#xff0c;在深度學習過程中&#xff0c;常用的 python 語法內容 2、學習內容 2.1、pytorch 常見語法 2.…

力扣網C語言編程題:搜索二維矩陣(右上角->左下角解法)

一. 簡介 上一篇文章關于"在二維數組中查找某個元素"的問題&#xff0c;提供了兩種解題思路&#xff0c;文章如下&#xff1a; 力扣網C語言編程題&#xff1a;搜索二維矩陣的普通解法與二分查找法-CSDN博客 本文提供第三種解題思路&#xff1a;從左下角->右上角…