如何使用Google Test对C++ gRPC异步客户端函数进行单元测试
gRPC异步RPC Mock的GMOCK匹配失败问题解决
核心问题原因
你遇到的Uninteresting mock function call警告本质是GMOCK收到AsyncfooRaw调用时,没有找到匹配的EXPECT_CALL规则,最常见的两个诱因:
- lambda返回值类型与
MOCK_METHOD声明的返回类型不匹配:生成的Mock方法返回的是::grpc::ClientAsyncResponseReaderInterface<::FooReply>*,你写的lambda返回类型是grpc_impl::ClientAsyncResponseReaderInterface<::FooReply>*,GMOCK对类型匹配要求严格,不一致会导致规则不命中。 - 参数匹配规则不符合实际调用的参数,如果你没有显式指定参数匹配规则,GMOCK默认要求参数完全相等,指针类型参数会比较地址,很容易出现匹配失败。
解决步骤
1. 定义Mock异步响应读取器
异步RPC的返回值是ClientAsyncResponseReaderInterface的实例,你需要先Mock这个类来模拟异步返回逻辑:
#include <gmock/gmock.h> #include <grpcpp/grpcpp.h> template <typename ResponseType> class MockClientAsyncResponseReader : public grpc::ClientAsyncResponseReaderInterface<ResponseType> { public: MOCK_METHOD(void, StartCall, (), (override)); MOCK_METHOD(void, ReadInitialMetadata, (void* tag), (override)); MOCK_METHOD(void, Finish, (ResponseType* response, grpc::Status* status, void* tag), (override)); };
2. 编写正确的EXPECT_CALL规则
- 用
testing::_通配符匹配任意参数,避免地址匹配失败 - lambda返回值类型与Mock声明完全一致
- 同时设置
Finish方法的期望,模拟异步返回的结果
// 测试用全局/局部CompletionQueue实例,要和业务代码用的是同一个 grpc::CompletionQueue cq; // 创建Mock读取器实例,生命周期要覆盖整个异步调用流程 MockClientAsyncResponseReader<FooReply> mockReader; // 设置AsyncfooRaw的期望 EXPECT_CALL(mockStub, AsyncfooRaw(testing::_, testing::_, testing::_)) .Times(1) .WillOnce([&mockReader](::grpc::ClientContext* context, const ::EmptyMessage& request, ::grpc::CompletionQueue* cq) -> ::grpc::ClientAsyncResponseReaderInterface<::FooReply>* { // 这里可添加参数校验逻辑,比如检查request的字段是否符合预期 return &mockReader; }); // 设置Finish方法的期望,模拟异步返回结果 FooReply expectedReply; // 给预期返回值赋值 expectedReply.set_result_code(200); expectedReply.set_msg("success"); EXPECT_CALL(mockReader, Finish(testing::_, testing::_, testing::_)) .Times(1) .WillOnce([&expectedReply, &cq](FooReply* resp, grpc::Status* status, void* tag) { // 把预期返回值赋值给响应指针 *resp = expectedReply; *status = grpc::Status::OK; // 把tag推入CompletionQueue,触发业务侧的异步回调逻辑 cq.Push(tag, true); });
注意事项
- 保证设置期望的
mockStub实例就是业务代码实际调用的stub实例,不要创建两个独立的Mock对象 mockReader实例的生命周期要足够长,要等到业务代码调用完Finish之后再析构,避免野指针问题- 如果需要校验特定参数,可以把
testing::_替换成具体的匹配规则,比如testing::Pointee(xxx)来匹配指针指向的内容
内容的提问来源于stack exchange,提问作者jviita
相关产品推荐
相关产品推荐

