SRS 中的 gMock(Google Mock)C++ 模拟框架实战指南

发布时间:2026/9/10 15:23:24
SRS 中的 gMock(Google Mock)C++ 模拟框架实战指南 SRS 中的 gMockGoogle MockC 模拟框架实战指南【免费下载链接】srsSRS is a simple, high-performance, AI-driven real-time media server supporting RTMP, WebRTC, HLS, HTTP-FLV, HTTP-TS, SRT, MPEG-DASH, and GB28181, with codec support for H.264, H.265, AV1, VP9, AAC, Opus, and G.711.项目地址: https://gitcode.com/GitHub_Trending/sr/srs本指南以 SRS 仓库内置的 GoogleMock 框架gMock为核心介绍如何利用其声明式语法编写 C mock 类、验证函数调用与参数、控制 mock 行为并展示 SRS 单元测试utest中真实使用 gMock 模式构造的 Mock 类与测试骨架帮助你为实时媒体服务器这类高并发、强时序的系统编写可维护的单元测试。概述gMock 是什么gMockGoogletest Mocking Framework是 Google 为 C 编写和使用 mock 类而设计的框架用于在单元测试中隔离被测对象与真实依赖。正如其在仓库中的 README 所述它可以帮助开发者derive better designs of your system and write better tests推导更好的系统设计并写出更好的测试。其设计灵感来源于 jMock、EasyMock 与 Hamcrest 等测试框架并针对 C 的特性做了专门设计。在 SRS 仓库中gMock 与 gtest 一起被打包在 trunk/3rdparty/gtest-fit/ 目录下googlemock/即 gMock 本体googletest/为 gtest 基础框架SRS 的所有 C 单元测试trunk/src/utest/均构建于此框架之上。gMock 的核心能力gMock 提供以下关键能力见 README.md能力说明声明式 mock 定义语法通过MOCK_METHOD宏族声明 mock 方法无需手写桩代码部分混合mockmock 对象可以是真实对象与 mock 的混合体任意类型与重载函数支持任意函数类型和重载函数丰富的匹配器matchers用于校验函数参数是否符合预期直观的行为控制语法通过EXPECT_CALL设定 mock 行为自动期望校验无需 record-and-replay测试结束时自动校验期望是否满足任意部分调用顺序约束可以表达函数调用之间的顺序约束可扩展性用户可自定义新的 matcher 与 action不使用异常整个框架不依赖 C 异常机制易学易用语法直观学习成本低这些能力在 SRS 的单元测试代码中有大量直接体现例如 srs_utest_manual_mock.hpp 中定义了MockSdpFactory、MockRtcSourceManager、MockLiveSource、MockAppStatistic等几十个 Mock 类覆盖了 SDP 生成、RTC 源管理、统计、安全校验等媒体服务器核心模块。gMock 在仓库中的目录结构gMock 本体位于trunk/3rdparty/gtest-fit/googlemock/googlemock/ ├── CMakeLists.txt # CMake 构建脚本 ├── README.md # gMock 官方说明本文档主体 ├── include/gmock/ │ ├── gmock.h # 主头文件包含全部 gMock 能力 │ ├── gmock-actions.h # action动作定义 │ ├── gmock-cardinalities.h # 调用次数基数约束 │ ├── gmock-function-mocker.h # MOCK_METHOD 宏族的实现 │ ├── gmock-matchers.h # matcher匹配器定义 │ ├── gmock-more-actions.h # 扩展 action │ ├── gmock-more-matchers.h # 扩展 matcher │ ├── gmock-nice-strict.h # NiceMock/NaggyMock/StrictMock │ ├── gmock-spec-builders.h # EXPECT_CALL 期望构建器 │ └── internal/ # 内部实现细节 └── src/ ├── gmock-all.cc # 编译入口 ├── gmock-cardinalities.cc ├── gmock-internal-utils.cc ├── gmock-matchers.cc ├── gmock-spec-builders.cc ├── gmock.cc └── gmock_main.cc # 提供 main() 入口各头文件职责明确gmock-function-mocker.h负责MOCK_METHOD声明宏gmock-spec-builders.h负责EXPECT_CALL期望设置gmock-matchers.h提供参数匹配器gmock-nice-strict.h提供 mock 的宽松/警告/严格三种模式。定义 Mock 类声明式语法gMock 的核心是MOCK_METHOD宏族。对于 C 传统语法SRS 使用的 gtest-fit 版本同时支持MOCK_METHODn旧式与MOCK_METHOD新式两种写法例如#include gmock/gmock.h class MockFoo { public: // 新式语法返回类型 方法名 参数列表 MOCK_METHOD(int, Bar, (const std::string input)); // 旧式语法传统写法 // MOCK_METHOD1(Bar, int(const std::string input)); };声明式 vs 手写桩代码传统的手写 mock 需要为每个接口方法实现一份空壳代码记录参数、返回默认值、维护调用计数代码量巨大且容易出错。gMock 的MOCK_METHOD宏自动生成这些样板代码你只需要在类中声明与真实接口签名一致的方法用MOCK_METHOD替换普通方法声明在测试中通过EXPECT_CALL声明期望。从 SRS 源码可以看到这一模式的应用。例如 srs_utest_manual_mock.hpp 中MockRtcSourceManager继承自ISrsRtcSourceManager接口将initialize()、fetch_or_create()、fetch()三个接口方法替换为带计数与可注入错误的实现class MockRtcSourceManager : public ISrsRtcSourceManager { public: srs_error_t initialize_error_; srs_error_t fetch_or_create_error_; int initialize_count_; int fetch_or_create_count_; SrsSharedPtrSrsRtcSource mock_source_; public: MockRtcSourceManager(); virtual ~MockRtcSourceManager(); virtual srs_error_t initialize(); virtual srs_error_t fetch_or_create(ISrsRequest *r, SrsSharedPtrSrsRtcSource pps); virtual SrsSharedPtrSrsRtcSource fetch(ISrsRequest *r); void set_initialize_error(srs_error_t err); void set_fetch_or_create_error(srs_error_t err); };部分混合MockgMock 支持部分 mock——即真实对象与 mock 的混合。这在 SRS 中体现为两类模式继承真实实现并覆盖特定方法如 srs_utest_manual_mock.cpp 中的MockRtcSource继承自SrsRtcSource只覆写on_rtp()以增加音视频包计数其余逻辑仍然走真实实现srs_error_t MockRtcSource::on_rtp(SrsRtpPacket *pkt) { on_rtp_count_; if (pkt-frame_type_ SrsFrameTypeAudio) { rtp_audio_count_; } else if (pkt-frame_type_ SrsFrameTypeVideo) { rtp_video_count_; } return SrsRtcSource::on_rtp(pkt); // 调用真实实现 }实现接口的空壳 计数 错误注入如MockAppStatistic实现ISrsStatistic接口的全部方法多数返回默认值但on_client()记录调用次数、保存最后参数并支持注入错误码set_on_client_error()以便测试各种失败分支。期望与验证EXPECT_CALLEXPECT_CALL是 gMock 的行为控制核心语法为EXPECT_CALL(mock_object, MethodName(matchers...)) .Times(cardinality) // 期望调用次数 .WillOnce(action) // 第一次调用时的行为 .WillRepeatedly(action) // 后续调用行为 .RetiresOnSaturation(); // 期望满足后失效与 record-and-replay 模式不同gMock 采用自动验证测试结束mock 对象析构时gMock 自动检查所有EXPECT_CALL是否被满足调用次数是否达标无需手动进入验证模式。次数约束CardinalitiesTimes()用于约束调用次数写法含义.Times(3)恰好调用 3 次.Times(AtLeast(1))至少调用 1 次.Times(AtMost(2))最多调用 2 次.Times(Between(1, 3))调用 1 到 3 次.Times(AnyNumber())任意次数省略 Times默认恰好调用 1 次次数约束的实现位于 gmock-cardinalities.h 与 gmock-cardinalities.cc。行为设置ActionsWillOnce/WillRepeatedly设置调用时的返回行为EXPECT_CALL(mock, GetName()) .WillOnce(Return(srs)) // 第一次返回 srs .WillRepeatedly(Return(rtmp)); // 之后一直返回 rtmp EXPECT_CALL(mock, Process(_, _)) .WillOnce(DoAll(SaveArg0(captured), Return(true))); // 保存参数并返回 true常用 action 包括Return(value)、ReturnNull()、Throw(exception)gMock 本身不用异常但允许被测代码抛、SaveArgN()、Invoke(functor)等定义于 gmock-actions.h 与 gmock-more-actions.h。匹配器Matchers匹配器用于校验函数实参定义于 gmock-matchers.h匹配器作用_匹配任意值Eq(x)/Ne(x)等于 / 不等于Lt(x)/Le(x)/Gt(x)/Ge(x)大小比较StrEq(s)/StrNe(s)字符串相等 / 不等HasSubstr(s)包含子串StartsWith(s)/EndsWith(s)前缀 / 后缀IsNull()/NotNull()空指针判断ElementsAre(...)/UnorderedElementsAre(...)容器元素匹配AllOf(m1, m2)/AnyOf(m1, m2)组合匹配顺序约束gMock 允许表达调用顺序约束InSequence与Sequence这对于 SRS 这类协议时序敏感的系统非常关键。例如推流测试要求必须先connect_app()再start_publish()InSequence seq; EXPECT_CALL(mock, Connect()).Times(1); EXPECT_CALL(mock, Publish()).Times(1); // 必须发生在 Connect 之后NiceMock、NaggyMock 与 StrictMock默认情况下gMock 对无期望的调用uninteresting calls会打印警告即 Naggy 行为。三种模式定义于 gmock-nice-strict.h包装类无期望调用时的行为NiceMockMockFoo静默忽略返回默认值测试更易维护NaggyMockMockFoo打印警告当前默认行为StrictMockMockFoo视为测试失败严格模式用法using ::testing::NiceMock; using ::testing::StrictMock; NiceMockMockFoo nice_foo; // 宽松 StrictMockMockFoo strict_foo; // 严格源码注释gmock-nice-strict.h特别说明NiceMock、NaggyMock、StrictMock会继承基类构造函数因此可以直接传参构造例如NiceMockMockFoo(5, a)已知限制只能作用于直接在类中通过MOCK_METHOD宏定义的方法嵌套使用三种包装不支持。gMock 的扩展性gMock 允许用户通过自定义 matcher 与 action 扩展框架自定义 matcher使用MATCHER(IsValidUrl, url is valid) { return ...; }宏或实现MatcherT接口自定义 action使用ACTION(MyAction) { ... }宏或实现ActionT。这与 SRS 中大量手写的计数 错误注入辅助方法如set_initialize_error()、set_fetch_or_create_error()互补前者偏重行为声明后者偏重状态断言。SRS 中 gMock 的实际应用utest 测试框架测试入口与框架初始化SRS 的单元测试二进制入口在 srs_utest.cpp// Copy from gtest-1.6.0/src/gtest_main.cc GTEST_API_ int main(int argc, char **argv) { ... testing::InitGoogleTest(argc, argv); int r0 RUN_ALL_TESTS(); return r0; }入口调用testing::InitGoogleTest与RUN_ALL_TESTSgtest 宏并在prepare_main()中完成 SRS 全局对象初始化、DTLS 证书初始化、SRT 日志抑制等准备工作随后运行全部测试用例。所有测试源文件都包含公共头文件 srs_utest.hpp其中#include gtest/gtest.h引入 gtest而全部 Mock 类则统一从#include srs_utest_manual_mock.hpp引入。测试骨架与宏SRS 的每个测试文件如srs_utest_ai01.cpp、srs_utest_manual_rtmp.cpp都使用 gtest 的TEST/TEST_F宏组织用例。公共头文件 srs_utest.hpp 还封装了针对 SRS 错误体系srs_error_t的断言辅助宏#define HELPER_EXPECT_SUCCESS(x) // 期望返回 srs_success失败时打印错误描述 #define HELPER_EXPECT_FAILED(x) // 期望返回错误 #define HELPER_ASSERT_SUCCESS(x) // 断言成功不继续执行 #define HELPER_ASSERT_FAILED(x) // 断言失败这些宏内部调用EXPECT_TRUE/ASSERT_TRUE是 gMock/gtest 断言体系在 SRS 项目中的封装。Mock 类在测试中的典型用法以 srs_utest_manual_mock.cpp 为例可以看到 SRS 用手工 mock覆盖了这些场景构造测试输入MockSdpFactory生成 Chrome 风格audio 在前与 libdatachannel 风格video 在前的真实 WebRTC SDP Offer覆盖 H.264 Opus、AV1、VP9、G.711 PCMU 等多种编码组合用于驱动 RTC 连接解析测试MockSdpFactory::MockSdpFactory() { audio_ssrc_ 1001; audio_pt_ 111; video_ssrc_ 2002; video_pt_ 96; } std::string MockSdpFactory::create_chrome_publisher_offer_with_h264() { // 生成包含 ICE、DTLS fingerprint、SSRC 的完整 SDP见源码 L107-L156 ... }替换真实依赖MockRtcSourceManager、MockLiveSourceManager、MockSrtSourceManager等替代真实的 Source 管理器通过mock_source_返回预先构造的SrsSharedPtr并通过set_*_error()注入错误覆盖推流/拉流失败分支srs_error_t MockRtcSourceManager::fetch_or_create(ISrsRequest *r, SrsSharedPtrSrsRtcSource pps) { srs_error_t err srs_success; if (fetch_or_create_count_ 0) { err mock_source_-initialize(r); } fetch_or_create_count_; if (fetch_or_create_error_ ! srs_success) { return srs_error_copy(fetch_or_create_error_); } pps mock_source_; return err; }验证调用序列与参数MockRtmpServer实现ISrsRtmpServer接口对 RTMP 握手的每个阶段handshake、connect_app、start_play、start_publish都维护独立的调用计数与可注入错误测试可断言恰好调用 N 次或参数是否正确。MockAppStatistic::on_client()则记录last_client_req_、last_client_conn_、last_client_type_供测试校验传入的请求对象。从手写 mock 到 gMock 的演进思路SRS 的 utest 采用了手写接口 mock gtest 断言的组合这体现了两种风格的互补手写 mock 的优势是可控性强、无宏展开开销适合需要精确统计调用次数、保存参数快照的复杂接口如ISrsStatistic、ISrsRtmpServer原生 gMock 的优势是声明式、代码量少适合接口方法多、只需简单返回值的场景。在阅读 srs_utest_manual_mock.cpp 时可以对照 gMock 的EXPECT_CALL思维每个xxx_count_字段对应Times()约束每个set_xxx_error()对应WillOnce(Return(err))每个last_xxx_字段对应SaveArg。理解这种对应关系就能把 SRS 现有测试改写成更声明式的 gMock 风格或在新模块测试中直接采用 gMock。构建与运行 SRS 单元测试SRS 的构建脚本为 gtest 生成了独立的 Makefile见 trunk/auto/utest.sh。该脚本的关键逻辑指定 gtest 目录GTEST_DIR../3rdparty/gtest/googletest相对objs/utest目录强制使用 C11SRS_CPP_VERSION-stdc11gtest 运行所需生成gtest.a与gtest_main.a分别对应用户自带 main()与使用 gtest_main 默认 main()两种链接方式SRS 使用自带 main() 的方式见上文srs_utest.cpp中的main因此链接gtest.a而非gtest_main.a将MODULE_FILES即srs_utest_ai01srs_utest_ai24、srs_utest_manual_*等测试源文件逐一编译为.o与全部 SRS 源码目标文件一起链接生成最终二进制。运行测试的基本流程cd trunk ./configure --uteston # 开启单元测试构建 make # 编译会生成 objs/srs_utest 等 ./objs/srs_utest # 运行全部测试用例--uteston会通过 srs_core.hpp 中SRS_FORCE_PUBLIC4UTEST宏自动启用#define private public/#define protected public见 srs_utest.hpp 注释使测试代码能直接访问被测类的私有成员这是 SRS 为单元测试深度定制的特性。gMock 的使用前提与限制C11 及以上本仓库的 gtest-fit 要求 C11见 utest.sh不使用异常gMock 自身不依赖异常适用于-fno-exceptions的构建环境严格模式限制NiceMock/StrictMock只对直接声明于本类的MOCK_METHOD生效嵌套使用不受支持见 gmock-nice-strict.hCMake 构建gMock 自身提供 CMakeLists.txtgmock与gmock_main两个目标可用-Dgmock_build_testsON构建其自带测试但 SRS 主项目使用configuremake构建体系二者相互独立。总结gMock 为 C 单元测试提供了声明式的 mock 定义、丰富的参数匹配器、直观的行为控制与自动的期望验证并能通过NiceMock/NaggyMock/StrictMock调节测试严格度。在 SRS 仓库中gMock 与 gtest 是全部单元测试的地基测试入口srs_utest.cpp、公共断言宏srs_utest.hpp以及覆盖 RTC/RTMP/SRT 等核心模块的大量 Mock 类srs_utest_manual_mock.cpp、srs_utest_manual_mock.hpp共同构成了媒体服务器的高质量测试体系。掌握 gMock 的声明式思维不仅可以直接复用 SRS 的测试基建还能为后续新功能编写更精炼、更可维护的测试代码。【免费下载链接】srsSRS is a simple, high-performance, AI-driven real-time media server supporting RTMP, WebRTC, HLS, HTTP-FLV, HTTP-TS, SRT, MPEG-DASH, and GB28181, with codec support for H.264, H.265, AV1, VP9, AAC, Opus, and G.711.项目地址: https://gitcode.com/GitHub_Trending/sr/srs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考