如何用GMock捕获const char*类型Mock函数参数?有无更简便方法?
问题描述
我需要Mock函数http_post的第二个const char*类型参数,用来构建JSON并验证指定元素的值。目前用定义http_post_args结构体结合GMock的Invoke方法实现了需求,但过程繁琐。之前试了SaveArgPointee只复制了字符串首字符,用ACTION_P时没法正确获取参数,对ACTION_TEMPLATE也没搞懂。想问这种结构体方法是不是唯一可行的?能不能给个用ACTION_P或ACTION_TEMPLATE捕获const char*的可行示例?
相关代码
Mock类定义:
class web_api_mock : public iweb_api { public: MOCK_METHOD( (bool), http_post, (const etl_normal_string &, const char*), (override)); };
现有实现代码:
struct http_post_args { void capture(etl_normal_string url, const char * p) { payload = std::string(p); } std::string payload; }; http_post_args args; EXPECT_CALL(_web_api_mock, http_post(etl_string_equals(url), _)) .WillOnce( DoAll( Invoke(&args, &http_post_args::capture), Return(true)));
尝试过的ACTION_P代码:
ACTION_P2(capture_string, url, payload) { /* 调试时上层栈帧可见目标字符串,但此处无法访问 */ }
解决方案
结构体+Invoke不是唯一方案,ACTION_P和ACTION_TEMPLATE都能实现更简洁的参数捕获,问题出在你之前的ACTION写法有误。
方法一:用ACTION_P捕获指定参数
GMock的ACTION宏内部可以通过arg0、arg1等内置变量直接访问Mock函数的参数(按声明顺序索引,从0开始)。正确的写法是把存储捕获结果的变量作为ACTION的参数传入,而非把要捕获的参数作为模板参数:
// 定义ACTION_P:接收一个std::string引用,用来存储捕获到的payload ACTION_P(SavePayload, target_str) { // arg1对应http_post的第二个参数(const char*类型) target_str = std::string(arg1); } // 使用示例 std::string captured_payload; EXPECT_CALL(_web_api_mock, http_post(etl_string_equals(url), _)) .WillOnce(DoAll( SavePayload(std::ref(captured_payload)), // 必须用std::ref传递引用,避免值拷贝 Return(true) )); // 后续即可用captured_payload构建JSON并验证元素
方法二:用ACTION_TEMPLATE实现通用参数捕获
如果需要灵活捕获任意位置的参数,ACTION_TEMPLATE是更通用的选择,它支持通过模板参数指定参数索引:
// 定义ACTION_TEMPLATE:模板参数指定要捕获的参数索引,值参数指定存储目标的指针 ACTION_TEMPLATE(SaveNthArg, HAS_2_TEMPLATE_PARAMS(int, kArgIndex, typename, T), AND_1_VALUE_PARAMETER(target)) { // args是Mock函数的参数元组,std::get<kArgIndex>取出对应位置的参数 *target = std::string(std::get<kArgIndex>(args)); } // 使用示例:捕获第二个参数(索引为1) std::string captured_payload; EXPECT_CALL(_web_api_mock, http_post(etl_string_equals(url), _)) .WillOnce(DoAll( SaveNthArg<1>(&captured_payload), Return(true) ));
为什么SaveArgPointee会失败?
SaveArgPointee<1>(&captured_payload)的行为是直接复制参数的解引用值:对于const char* p,它会复制*p(即字符串的第一个字符),而非整个字符串。因此要捕获完整字符串,必须手动将const char*转换为std::string,这也是上述ACTION实现中必须做的步骤。
内容的提问来源于stack exchange,提问作者bas

