GMock测试Libcurl单元测试报EXPECT_CALL未调用跨版本异常
GMock测试C实现curl接口Mock失效问题排查方案
核心根因
- Mock对象方法未被实际调用:
EXPECT_CALL绑定的是CurlMocker类实例的成员方法,测试用例中直接调用全局真实curl_fetch_urlC函数,从未触发Mock对象方法,必然报「期望调用1次,实际0次」错误。 - Mock方法签名不匹配:
CurlMocker中定义的curl_fetch_url第一个参数为void*,且错误使用MOCK_CONST_METHOD3添加const修饰,与真实C函数签名不一致,CMock无法自动完成桩替换。 - 测试用例参数错误:第二个用例调用
curl_fetch_url时误将mock对象作为第一个参数传入,不符合函数CURL*类型的入参要求。 - 跨版本差异原因:Oracle Linux 7默认编译链接选项宽松,Mock桩碰巧替换了真实函数符号;Oracle Linux 8默认开启PIE、符号可见性保护等安全编译选项,符号优先级规则变化导致桩注入失败,代码直接走真实libcurl逻辑,因此出现请求超时返回
CURLE_OPERATION_TIMEDOUT(28)的现象。 - 被测代码自带逻辑bug:
presponse->payload = (char *)calloc(1, sizeof(presponse->payload));中分配内存大小写为sizeof(presponse->payload),实际计算的是char*指针长度(64位系统下为8字节),并非payload实际存储长度,后续写入响应内容时会触发缓冲区溢出。
修复步骤
- 修正Mock方法定义,保证签名与真实C函数完全一致
修改curl_mocker.h中Mock类代码:class CurlMocker : public CMockMocker<CurlMocker> { public: // 第一个参数类型对齐真实函数,去掉错误的const修饰,使用MOCK_METHOD3 MOCK_METHOD3(curl_fetch_url, int(CURL*, const char*, response_t*)); }; - 修正测试用例逻辑,正确使用CMock桩替换能力
CMock会自动拦截全局C函数调用,不需要手动传入mock对象作为函数参数,也不需要真实初始化libcurl(避免发起真实网络请求)。
空入参测试用例修正后:
正常URL请求测试用例修正后:TEST(curl_rest_call, curl_fetch_url_1) { #if !defined(__MINGW32__) int expected_ret = NULL_POINTER; CurlMocker mock; EXPECT_CALL(mock, curl_fetch_url(nullptr, nullptr, nullptr)) .WillOnce(Return(NULL_POINTER)); // 直接调用全局C函数,CMock会自动路由到Mock方法 int actual_ret = curl_fetch_url(nullptr, nullptr, nullptr); ASSERT_EQ(actual_ret, expected_ret); #endif }TEST(curl_rest_call, curl_fetch_url_2) { #if !defined(__MINGW32__) int expected_ret = CURLE_OK; const char *url = "https://someurl.com/posts/1"; response_t response{}; response.payload = nullptr; response.size = 0; CurlMocker mock; // 入参匹配不要直接比对栈指针地址,用匹配器校验参数合法性 EXPECT_CALL(mock, curl_fetch_url(testing::_, testing::StrEq(url), testing::_)) .WillOnce(testing::Invoke([&](CURL* curl, const char* u, response_t* resp) -> int { // 模拟接口返回,填充测试用的响应数据 resp->size = 2; resp->payload = (char*)calloc(3, 1); memcpy(resp->payload, "ok", 2); return CURLE_OK; })); // 禁止传入mock对象作为第一参数,Mock场景下不会真实调用curl逻辑,传任意CURL*类型值即可 int actual_ret = curl_fetch_url((CURL*)0x1234, url, &response); ASSERT_EQ(actual_ret, expected_ret); ASSERT_STREQ(response.payload, "ok"); free(response.payload); #endif } - 适配Oracle Linux 8编译选项
编译测试用例时添加如下链接选项,保证CMock可以正常替换全局函数符号:LDFLAGS += -Wl,--allow-multiple-definition -no-pie -rdynamic - 修复被测代码内存分配bug
将固定大小分配改为curl写回调函数中按实际接收长度动态扩容,避免缓冲区溢出。
内容的提问来源于stack exchange,提问作者Vikas
相关产品推荐
相关产品推荐

