不能直接 new 网络对象,因为会真实发请求,破坏单元测试的隔离性;应通过接口抽象(如 inetworkclient)+ 智能指针注入实现解耦,用 mocknetworkclient 预设响应、模拟异常、控制重试,并在析构时断言调用次数。

为什么不能直接 new 网络对象?
因为 new HttpClient 或 curl_easy_init() 会真实发请求,单元测试就不是“单元”了——它依赖网络、服务端状态、DNS、超时配置,甚至同事刚删了测试环境。更糟的是,你没法控制返回值:想测“服务器返回 503”或“连接被拒绝”,得先黑进机房拔网线。
怎么让网络层可替换?用接口 + 指针/引用传入
核心是把具体实现和调用方解耦。c++ 没有语言级 DI 容器,但靠抽象基类 + 智能指针就能做到。关键不是“注入”这个词,而是谁负责创建、谁负责使用要分开。
-
INetworkClient定义纯虚函数,比如sendRequest(const std::String& url) - 真实实现叫
RealNetworkClient,继承它并调用 libcurl / Boost.Beast - 被测类(如
UserSyncService)只持有一个std::unique_ptr<inetworkclient></inetworkclient>,构造时传入 - 测试时传
MockNetworkClient,它内部用std::map预设 URL → 返回体,不碰 socket
mock 实现里最容易漏掉的三件事
很多人写完 mock 就跑测试,结果发现超时逻辑没覆盖、重试次数不对、或者异常路径根本没走。问题不在语法,而在契约理解偏差。
- 必须模拟
std::system_error抛出场景,比如MockNetworkClient::sendRequest在特定 URL 下 throwstd::system_error(std::errc::connection_refused) - 如果真实 client 有重试机制,mock 要暴露
setRetryCount(int),否则你测的永远是“一次成功”分支 - 别忽略移动语义:mock 对象被 move 进 service 后,原变量失效,再调它的方法会 crash —— 测试里别复用 mock 实例
std::shared_ptr vs std::unique_ptr?看生命周期所有权
用 std::unique_ptr 是默认选择。除非你明确需要多个组件共享同一 mock 实例(比如一个 service 和它的 logger 都要访问同一个计数器),才升到 std::shared_ptr。后者带来引用计数开销,且容易因循环引用导致 mock 不析构,断言失败时还查不出原因。
立即学习“C++免费学习笔记(深入)”;
另外,别在 mock 里用 std::this_thread::sleep_for 模拟延迟——它会让整个测试变慢,且掩盖异步逻辑缺陷。真要测超时,改 mock 的返回时间戳 + service 内部用 steady_clock 判断即可。
最常被跳过的一步:mock 的析构函数里加 assert(m_called_times == m_expected_calls)。不然你以为覆盖了所有路径,其实某个分支压根没触发。