Go中GRPC流Send函数测试:如何触发Send失败场景
问题:如何在真实gRPC服务端/客户端环境下触发Send方法失败进行测试
我有一个Go语言的gRPC服务端流函数:
func (server *Server) GetClients(req *iam.GetClientsRequest, client iam.IAM_GetClientsServer) error { ctx := client.(interface{ Context() context.Context }).Context() userID, err := getUserIDStream(client) if err != nil { return err } clients, err := server.db.QueryByUserID(ctx, userID) if err != nil { return grpc.Errorf(codes.Internal, apiutils.ServerError) } for _, value := range clients { converted, err := server.fromInternalClient(value) if err != nil { return err } if err := client.Send(converted); err != nil { return err } } return nil }
我的测试代码如下:
It("GetClients - Send fails - Error", func() { handler := createHandler(db) lis := bufconn.Listen(bufSize) server := grpc.NewServer() iam.RegisterIAMServer(server, NewServer(handler)) go func() { if err := server.Serve(lis); err != nil { log.Fatalf("Server exited with error: %v", err) } }() defer lis.Close() defer server.GracefulStop() conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(createBufDialier(lis)), grpc.WithInsecure()) Expect(err).ShouldNot(HaveOccurred()) defer conn.Close() client := iam.NewIAMClient(conn) cclient, _ := client.GetClients(addAccessToken(context.Background()), new(iam.GetClientsRequest)) resp, err := cclient.Recv() Expect(resp).Should(BeNil()) Expect(err).Should(HaveOccurred()) Expect(err.Error()).Should(Equal(message)) })
我遇到的问题是:不清楚如何触发Send方法的失败来测试响应。由于我使用的是真实的测试服务端和客户端,不想采用Mock方式,请问有没有可行的办法?
解决方法
方案1:主动关闭客户端连接
在客户端获取到流客户端后,立即关闭gRPC连接,服务端执行Send操作时会因为连接已断开而返回错误。修改后的测试代码如下:
It("GetClients - Send fails - Error", func() { handler := createHandler(db) lis := bufconn.Listen(bufSize) server := grpc.NewServer() iam.RegisterIAMServer(server, NewServer(handler)) go func() { if err := server.Serve(lis); err != nil { log.Fatalf("Server exited with error: %v", err) } }() defer lis.Close() defer server.GracefulStop() conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(createBufDialier(lis)), grpc.WithInsecure()) Expect(err).ShouldNot(HaveOccurred()) client := iam.NewIAMClient(conn) cclient, _ := client.GetClients(addAccessToken(context.Background()), new(iam.GetClientsRequest)) // 主动关闭连接,触发服务端Send失败 conn.Close() resp, err := cclient.Recv() Expect(resp).Should(BeNil()) Expect(err).Should(HaveOccurred()) Expect(err.Error()).Should(Equal(message)) })
方案2:取消客户端上下文
创建可取消的上下文传入GetClients,获取流客户端后立即取消上下文,服务端的Send操作会因为上下文被取消而失败。修改后的测试代码如下:
It("GetClients - Send fails - Error", func() { handler := createHandler(db) lis := bufconn.Listen(bufSize) server := grpc.NewServer() iam.RegisterIAMServer(server, NewServer(handler)) go func() { if err := server.Serve(lis); err != nil { log.Fatalf("Server exited with error: %v", err) } }() defer lis.Close() defer server.GracefulStop() conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(createBufDialier(lis)), grpc.WithInsecure()) Expect(err).ShouldNot(HaveOccurred()) defer conn.Close() client := iam.NewIAMClient(conn) // 创建可取消的上下文 ctx, cancel := context.WithCancel(addAccessToken(context.Background())) cclient, _ := client.GetClients(ctx, new(iam.GetClientsRequest)) // 立即取消上下文 cancel() resp, err := cclient.Recv() Expect(resp).Should(BeNil()) Expect(err).Should(HaveOccurred()) Expect(err.Error()).Should(Equal(message)) })
注意事项
- 不同gRPC版本返回的错误信息可能存在差异,需要根据实际运行结果调整断言的错误内容。
- 如果数据库返回的客户端数据量较大,也可以在服务端循环Send的过程中执行关闭连接或取消上下文的操作,同样能触发失败。
内容的提问来源于stack exchange,提问作者Woody1193
相关产品推荐
相关产品推荐

