如何Mock boost.asio库调用以实现C++ TCP客户端单元测试?
解决方案
针对你提到的约束:不新增myManager内部封装对象、使用turtle对象mock能力、避免全量手写boost.asio mock实现,可采用以下两种低成本方案:
方案1:符号劫持+测试侧薄Mock层(零生产代码侵入)
该方案完全不需要修改生产环境的myManager代码,所有改动仅在测试工程内生效:
- 测试工程编译时添加自定义宏标识(比如
UNIT_TEST_BUILD),同时屏蔽boost.asio静态库的链接,改为链接你自己实现的同名符号集 - 不需要全量mock boost.asio,仅针对myManager实际调用的
boost::asio::ip::tcp相关接口做薄转发层,所有调用转发到turtle的mock对象即可,示例代码如下:#ifdef UNIT_TEST_BUILD namespace boost::asio::ip { class tcp { public: class socket { public: // 仅声明myManager实际用到的方法,不需要实现全量asio接口 MOCK_METHOD2(connect, void(const endpoint&, boost::system::error_code&)); MOCK_METHOD2(write_some, size_t(const const_buffers_1&, boost::system::error_code&)); MOCK_METHOD1(close, void(boost::system::error_code&)); }; }; } #endif - 单元测试用例中直接对该mock类设置调用期望、返回值、错误码,即可验证myManager的内部逻辑:比如传入connect失败的错误码,验证getStatus()返回的状态是否符合预期,校验send_command是否只在连接成功后才调用write_some等。
方案2:模板参数注入(极低生产代码侵入,无运行时开销)
如果编译环境不支持符号劫持,可通过最小改动的模板注入实现mock,不需要新增内部成员对象:
- 仅修改myManager的定义为模板类,默认模板参数用原生boost.asio类型,原有业务逻辑完全不用改动:
// 生产代码仅需修改这一行定义,原有逻辑无任何变化 template<typename TcpProto = boost::asio::ip::tcp> class myManager { // 原有代码中所有tcp::socket替换为TcpProto::socket即可 }; - 测试侧实现符合接口要求的MockTcpProto类,内部用turtle定义mock方法,单元测试时实例化
myManager<MockTcpProto>即可完成测试。生产环境编译时模板使用默认参数,运行效率和原有代码完全一致。
两种方案都只需要mock你实际用到的10个以内的asio接口,开发成本极低,完全适配turtle仅支持对象mock的限制,也不需要新增myManager的内部封装对象。
内容的提问来源于stack exchange,提问作者Gionata Benelli
相关产品推荐
相关产品推荐

