C++中如何使用GoogleMock在mock函数中填充传入的结构体参数
GoogleMock填充accept预分配出参结构体的正确方法
问题本质
对int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);做mock时,直接使用SetArgPointee<1>(*sa)无法正确填充调用方预分配的sockaddr_storage结构体,核心原因有两点:
sa是通用struct sockaddr*类型,解引用赋值时只会拷贝sockaddr类型的长度(通常16字节),而sockaddr_in中存储端口、IP的字段位于该偏移之后,会被直接截断- 真实系统调用除了填充addr指向的内存,还会将
addrlen指向的值修改为实际写入的地址长度,原写法未同步更新该出参,不符合真实调用逻辑
推荐实现方式
使用Invoke自定义动作,直接操作mock函数接收到的指针参数,完全模拟真实accept的出参行为,不会出现类型截断问题。
- 提前在测试用例中定义预设的对端地址:
// 构造预期返回的对端地址 struct sockaddr_in preset_client{}; preset_client.sin_family = AF_INET; preset_client.sin_port = htons(50000); inet_pton(AF_INET, "192.168.1.2", &preset_client.sin_addr); const socklen_t preset_client_len = sizeof(preset_client);
- 编写EXPECT_CALL逻辑,在DoAll中通过Invoke完成内存拷贝和出参更新:
EXPECT_CALL(mocked_sys_socket, accept(listen_sock, _, NotNull())) .WillOnce(DoAll( Invoke([&](int, struct sockaddr* out_addr, socklen_t* out_addrlen) { // 向调用方预分配的内存拷贝完整的地址结构体 memcpy(out_addr, &preset_client, preset_client_len); // 同步更新地址长度为实际写入值,和内核行为一致 *out_addrlen = preset_client_len; }), Return(accept_sock) ));
简化写法(不推荐)
如果业务代码完全不读取accept返回后的addrlen值,也可以使用SetArrayArgument拷贝整块内存,不需要自定义lambda:
EXPECT_CALL(mocked_sys_socket, accept(listen_sock, _, Pointee(clientLen))) .WillOnce(DoAll( SetArrayArgument<1>( reinterpret_cast<const char*>(&preset_client), reinterpret_cast<const char*>(&preset_client) + preset_client_len ), Return(accept_sock) ));
该写法省略了addrlen的更新逻辑,和真实系统调用行为不一致,后续业务逻辑如果依赖addrlen的返回值会触发难以排查的测试问题。
内容的提问来源于stack exchange,提问作者Ingo
相关产品推荐
相关产品推荐

