如何用GoMock正确模拟gRPC服务器流式方法?解决客户端挂起问题
解决gRPC服务器流式方法的gomock模拟问题
你的问题出在三个核心点:
- 错误地将Mock期望语句(
rs.EXPECT().Send(...))直接传给Do方法——Do需要接收执行逻辑的函数,而非Mock期望声明。 - 未正确设置
FetchResponse方法的返回值:该方法最终必须返回error类型(通常返回nil表示流式响应正常结束)。 - 请求参数的精确匹配可能失效:如果客户端发送的
Request实例和你硬编码的&proto.Request{Id: 123}不是同一个对象,Mock的方法匹配会失败,导致逻辑不触发。
正确实现方式
核心步骤
- 为Mock Stream对象定义
Send方法的调用期望,指定要返回的响应内容,并确保发送操作成功(返回nil)。 - 在Mock Server的
FetchResponse期望中,通过Do传入执行函数触发响应发送,同时必须指定Return(nil)来结束流式调用。 - 使用参数匹配器避免实例不一致导致的匹配失败。
修正后的代码示例
func Test_Streaming(t *testing.T) { // 创建监听器 lis, err := net.Listen("tcp", ":50005") if err != nil { log.Fatalf("failed to listen: %v", err) } // 初始化gRPC服务器与Mock控制器 s := grpc.NewServer() ctrl := gomock.NewController(t) defer ctrl.Finish() // 测试结束时验证所有Mock期望是否触发 mockServer := mock_proto.NewMockStreamServiceServer(ctrl) proto.RegisterStreamServiceServer(s, mockServer) // 启动gRPC服务器 go func() { log.Println("start server") if err := s.Serve(lis); err != nil && !errors.Is(err, grpc.ErrServerStopped) { log.Fatalf("failed to serve: %v", err) } }() defer s.Stop() // 测试结束后关闭服务器,释放端口 // 1. 创建Mock Stream并设置Send方法期望 mockStream := mock_proto.NewMockStreamService_FetchResponseServer(ctrl) // 期望Send被调用1次,传入指定响应,返回nil表示发送成功 mockStream.EXPECT().Send(&proto.Response{Result: "steve"}).Times(1).Return(nil) // 2. 设置Mock Server的FetchResponse方法期望 mockServer.EXPECT().FetchResponse( gomock.Eq(&proto.Request{Id: 123}), // 用Eq做值匹配,避免实例不一致问题 mockStream, ).Times(1).Do(func(req *proto.Request, stream proto.StreamService_FetchResponseServer) { // 可选:手动调用Send触发响应,或者依赖上面的Mock期望自动执行 // stream.Send(&proto.Response{Result: "steve"}) }).Return(nil) // 必须返回error,nil表示流式调用正常结束 // 客户端调用FetchResponse ClientConnect() }
关键细节说明
- 参数匹配:用
gomock.Eq()包装请求参数,确保值相等即可匹配,而非要求完全相同的对象实例;若无需精确匹配,也可使用gomock.Any()。 - Mock控制器收尾:
defer ctrl.Finish()会在测试结束时检查所有Mock期望是否都被触发,帮你快速定位未执行的逻辑。 - Send方法返回值:
Send必须返回nil,否则会导致流式调用提前终止,客户端无法收到完整响应。
内容的提问来源于stack exchange,提问作者Steve Wu
相关产品推荐
相关产品推荐

