You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用GoMock正确模拟gRPC服务器流式方法?解决客户端挂起问题

解决gRPC服务器流式方法的gomock模拟问题

你的问题出在三个核心点:

  1. 错误地将Mock期望语句(rs.EXPECT().Send(...))直接传给Do方法——Do需要接收执行逻辑的函数,而非Mock期望声明。
  2. 未正确设置FetchResponse方法的返回值:该方法最终必须返回error类型(通常返回nil表示流式响应正常结束)。
  3. 请求参数的精确匹配可能失效:如果客户端发送的Request实例和你硬编码的&proto.Request{Id: 123}不是同一个对象,Mock的方法匹配会失败,导致逻辑不触发。

正确实现方式

核心步骤

  1. 为Mock Stream对象定义Send方法的调用期望,指定要返回的响应内容,并确保发送操作成功(返回nil)。
  2. 在Mock Server的FetchResponse期望中,通过Do传入执行函数触发响应发送,同时必须指定Return(nil)来结束流式调用。
  3. 使用参数匹配器避免实例不一致导致的匹配失败。

修正后的代码示例

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 13:46:04