go - 欺骗 grpc UnaryHandler 以在 Go 中对 gRPC 进行单元测试

标签 go testing grpc

我正在努力提高我的 Go gRPC 服务器的覆盖率,但我在为服务器的拦截器功能编写测试时遇到了麻烦,因为我无法有意义地满足 UnaryHandler类型。

我有一个函数 Interceptor具有以下签名:

Interceptor func(
  ctx context.Context,
  req interface{},
  info *grpc.UnaryServerInfo,
  handler grpc.UnaryHandler, // <- my issue comes from here
) (interface{}, error)

我假设任何 gRPC 方法都会满足 UnaryHandler 的签名:

type UnaryHandler func(ctx context.Context, req interface{}) (interface{}, error)

所以我尝试传入一个带有这个签名的方法:

GetToken(ctx context.Context, req *AuthData) (*Token, error)

我想这会起作用,因为这是拦截器实际正在做的事情(转发那个 RPC),但出于某种原因 Go 提示:

cannot use authService.GetToken (type func(context.Context, *AuthData) (*Token, error)) as type grpc.UnaryHandler in argument to Interceptor

我继续写了一个正确满足的虚拟函数:

func genericHandler(ctx context.Context, req interface{}) (interface{}, error) {
    return req, nil
}

这很好,因为我并不特别需要在测试拦截器时运行任何特定方法。但是,我很好奇为什么实际方法不满足约束,因为(根据我的理解)每当我在野外调用该 RPC 时,它都会被传递给引擎盖下的拦截器函数。

最可能的解释是 grpc UnaryHandler 没有按照我的想法去做,但是它做了什么?

最佳答案

不,函数

GetToken(ctx context.Context, req *AuthData) (*Token, error)

不是同一类型
type UnaryHandler func(ctx context.Context, req interface{}) (interface{}, error)

GetToken中,第二个参数req的类型是*AuthData,而在UnaryHandler中,req 属于 interface{} 类型。返回的 *Tokeninterface{} 的类型也不同。这就是为什么不能将 GetToken 直接传递给拦截器的原因。

在你的 grpc 服务中,你会写这样的方法

GetToken(ctx context.Context, req *AuthData) (*Token, error)

作为执行服务器工作的处理程序。然而,它并不像人们想象的那样是一个 UnaryHandler

大部分转换是由grpc/protobuf 代码生成器完成的。根据您的原型(prototype)定义,它会生成一个接口(interface),如下所示:

type XXXServer interface {
    GetToken(ctx context.Context, req *AuthData) (*Token, error)
}

您可以看到您的处理程序满足的是这个接口(interface)(而不是 UnaryHander)。

在幕后,如果您查看生成的 xxx.pb.go 文件,您会发现一些 _XXX_GetToken_Handler 实际上在执行处理程序工作。在此函数中,定义了一个(实际的)UnaryHandler,如下所示:

func _XXX_GetToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
    // skip other preparations...
    // 
    handler := func(ctx context.Context, req interface{}) (interface{}, error) {
        return srv.(XXXServer).GetToken(ctx, req.(*AuthData))
    }
    return interceptor(ctx, in, info, handler)
}

在这个 UnaryHandler 中,它会将您的服务器转换为 XXXServer 接口(interface),然后调用您的处理程序(您的代码)。这显示了如何调用 interceptor

关于go - 欺骗 grpc UnaryHandler 以在 Go 中对 gRPC 进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57681271/

相关文章:

javascript - 有没有办法在 node.js 中出现异常或测试失败时启动调试器?

django - 在 Django 中对执行外部服务调用的 View 进行单元测试时,什么是好的做法

go - 成功时返回 grpc 状态码

MongoDB 在 Go 中使用 mgo,使用 bson.M/bson.D 的运算符总是出现语法错误

arrays - 无法对二维数组的列进行 slice "cannot use Sudoku[0:9][0] (type [9]int) as type []int in assignment"

arrays - 为什么 *S Slice 可以由文字 S 开头?

php - 设置测试服务器简介

java - Maven 和 java : missing generated code for service in test directory

go - grpc中的多服务器

go - 无法构建简单的 golang 代码 - slice 文字语法