dousi6701 2019-08-27 19:42
浏览 162

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

I'm working on improving coverage for my Go gRPC server but I have run into trouble writing a test for the server's interceptor function because I'm unable to meaningfully satisfy the UnaryHandler type.

I have a function Interceptor with the following signature:

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

I assumed that any gRPC method would satisfy the signature of UnaryHandler:

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

So I tried passing in a method with this signature:

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

I imagined this would work, since this is what the Interceptor is actually doing (forwarding that RPC), but for some reason Go complains:

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

I went ahead and wrote a dummy function that correctly satisfies:

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

which is fine since I don't particularly need to run any specific method when testing the interceptor. However I am curious as to why the actual method doesn't satisfy the constraints because (according to my understanding) it is being passed to the Interceptor function under hood whenever I call that RPC in the wild.

The most likely explanation is that the grpc UnaryHandler doesn't do what I'm thinking it does, but then what does it do?

  • 写回答

1条回答 默认 最新

  • douchongbc72264 2019-09-01 09:52

    No, the function

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

    is not of the same type as

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

    In GetToken the second parameter req is of type *AuthData, while in UnaryHandler req is of type interface{}. The returned *Token is not the same type as interface{}, neither. This is the reason why you can't pass GetToken directly to the interceptor.

    In your grpc services, you would write methods like

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

    as handlers to do your server works. However, it's not an UnaryHandler as one may think.

    Most of the transformation is done by the grpc/protobuf code generator. Base on your proto definition, it generates an interface, like this:

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

    You can see that it is this interface (not the UnaryHander) that your handler satisfies.

    Under the hood, if you take a look on the xxx.pb.go file generated, you'll find some _XXX_GetToken_Handler that's actually doing the handler work. In this function, an (actual) UnaryHandler is defined, as:

    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)

    Inside this UnaryHandler, it will cast your server to the XXXServer interface, and then invoke your handler (your code). And that shows how the interceptor is called.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?



  • ¥15 Arduino,利用modbus的RS485协议,进行对外置的温湿度传感器进行数据读取
  • ¥15 vhdl+MODELSIM
  • ¥20 simulink中怎么使用solve函数?
  • ¥30 dspbuilder中使用signalcompiler时报错Error during compilation: Fitter failed,求解决办法
  • ¥15 gwas 分析-数据质控之过滤稀有突变中出现的问题
  • ¥15 没有注册类 (异常来自 HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG))
  • ¥15 知识蒸馏实战博客问题
  • ¥15 用PLC设计纸袋糊底机送料系统
  • ¥15 simulink仿真中dtc控制永磁同步电机如何控制开关频率
  • ¥15 用C语言输入方程怎么