如何使用 Go 中的方法模拟虚拟 grpc 服务

发布于 2025-01-10 16:44:26 字数 860 浏览 0 评论 0原文

我需要为一个函数编写一个单元测试,该函数在其源代码中调用不同 gRPC 服务的 gRPC 方法。我的测试结果出现严重错误,因为 gRPC 服务未在我的计算机上运行。我想在测试中创建一个虚拟服务,并且该服务必须为源代码内的方法调用提供服务。我该怎么做?

编辑:详细信息 源函数:

func (s *mainType) MainGenerateBearerToken(inputs...)(output) {
        response, err := s.Subtype.SubGenerateBearerToken(context, authnPass, ttl)
        if err != nil {
            return nil, err
        }
        return &models.OCITokenResponse{Token: response.Token, ExpiresIn: ttl}, nil
    }

func(cl *Subtype) SubGenerateBearerToken(context,authnPass, ttl) (*BearerTokenResponse, error) {
    
    resp,err := pb.NewGrpcClient(cl.Conn).GetBearerToken_grpc(ctx, &pb.GetBearerTokenRequest{AuthnToken, BearerTtlValue:ttlNum)
    
    return &BearerTokenResponse{Token}, err
}

我正在为 MainGenerateBearerToken() 函数编写测试,该函数调用 SubGenerateBearerToken(),其中调用 grpc 方法。

I need to write a unit test for a function, which in its source code calls a gRPC method of a different gRPC service. My test results in a panic error because that gRPC service is not running on my machine. I want to create a dummy service in my test and that service must serve the method calling inside the source code. How do I do that?

Edit: Details
Source function:

func (s *mainType) MainGenerateBearerToken(inputs...)(output) {
        response, err := s.Subtype.SubGenerateBearerToken(context, authnPass, ttl)
        if err != nil {
            return nil, err
        }
        return &models.OCITokenResponse{Token: response.Token, ExpiresIn: ttl}, nil
    }

func(cl *Subtype) SubGenerateBearerToken(context,authnPass, ttl) (*BearerTokenResponse, error) {
    
    resp,err := pb.NewGrpcClient(cl.Conn).GetBearerToken_grpc(ctx, &pb.GetBearerTokenRequest{AuthnToken, BearerTtlValue:ttlNum)
    
    return &BearerTokenResponse{Token}, err
}

I'm writing test for the MainGenerateBearerToken() function, which calls SubGenerateBearerToken(), inside which the grpc method is called.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

紅太極 2025-01-17 16:44:26

gRPC 服务客户端是一个常规的 Go 接口。

您可以使用 gomockmoq 为任何 Go 接口生成模拟。

$ go install github.com/golang/mock/[email protected]
$ mockgen -source=my_service.pb.go MyServiceClient

或者

$ go install github.com/matryer/[email protected]
$ moq -pkg mock -out mock/my_service.go . MyServiceClient

这将生成实现 MyServiceClientmock.MyServiceClientMock 。然后您可以在单元测试中使用它而不是真正的 gRPC 客户端。

使用起订量的完整示例

my_service.proto

syntax = "proto3";

package main;
option go_package = "main";

service MyService {
    rpc Hello(HelloMesage) returns (HelloMesage) {}
}

message HelloMesage {
  string msg = 1;
}

main.go

package main

import context "context"

//go:generate protoc --go_out=plugins=grpc:. -I=. my_service.proto
//go:generate moq -out my_service_mock.go . MyServiceClient

func CallHello(myService MyServiceClient) (*HelloMesage, error) {
    return myService.Hello(context.TODO(), &HelloMesage{Msg: "hello"})
}

func main() {
    // (omitted) call grpc normally ...
}

main_test.go

package main

import (
    context "context"
    "testing"

    "github.com/stretchr/testify/assert"
    grpc "google.golang.org/grpc"
)

func TestCallHello(t *testing.T) {
    myServiceClient := &MyServiceClientMock{
        HelloFunc: func(ctx context.Context, in *HelloMesage, opts ...grpc.CallOption) (*HelloMesage, error) {
            return &HelloMesage{Msg: in.Msg + " reply"}, nil
        },
    }
    res, err := CallHello(myServiceClient)
    assert.NoError(t, err)
    assert.Equal(t, "hello reply", res.Msg)

}

运行:

$ go mod init myservice
$ go mod tidy
$ go generate
$ go test

A gRPC service client is a regular Go interface.

You can use tools like gomock or moq to generate a mock for any Go interface.

$ go install github.com/golang/mock/[email protected]
$ mockgen -source=my_service.pb.go MyServiceClient

Or

$ go install github.com/matryer/[email protected]
$ moq -pkg mock -out mock/my_service.go . MyServiceClient

That will generate mock.MyServiceClientMock that implements MyServiceClient. Then you can use it in your unit tests instead of the real gRPC client.

Complete example using moq:

my_service.proto:

syntax = "proto3";

package main;
option go_package = "main";

service MyService {
    rpc Hello(HelloMesage) returns (HelloMesage) {}
}

message HelloMesage {
  string msg = 1;
}

main.go:

package main

import context "context"

//go:generate protoc --go_out=plugins=grpc:. -I=. my_service.proto
//go:generate moq -out my_service_mock.go . MyServiceClient

func CallHello(myService MyServiceClient) (*HelloMesage, error) {
    return myService.Hello(context.TODO(), &HelloMesage{Msg: "hello"})
}

func main() {
    // (omitted) call grpc normally ...
}

main_test.go:

package main

import (
    context "context"
    "testing"

    "github.com/stretchr/testify/assert"
    grpc "google.golang.org/grpc"
)

func TestCallHello(t *testing.T) {
    myServiceClient := &MyServiceClientMock{
        HelloFunc: func(ctx context.Context, in *HelloMesage, opts ...grpc.CallOption) (*HelloMesage, error) {
            return &HelloMesage{Msg: in.Msg + " reply"}, nil
        },
    }
    res, err := CallHello(myServiceClient)
    assert.NoError(t, err)
    assert.Equal(t, "hello reply", res.Msg)

}

To run:

$ go mod init myservice
$ go mod tidy
$ go generate
$ go test
木緿 2025-01-17 16:44:26

如果适合在 SubGenerateBearerToken 方法中删除 grpc 客户端创建(例如,将其保留在子类型中或作为参数传递),您可以轻松创建一个简单的假客户端:

type fakeClient struct {
    grpc.YourClient // a client interface from your *.pb.go file
}

func NewFakeClient(_ *grpc.ClientConn) *fakeClient {
    return &fakeClient{}
}

func (fc *fakeClient) GetBearerToken_grpc(ctx context.Context, in *pb.GetBearerTokenRequest, opts ...grpc.CallOption) (*pb.BearerTokenResponse, error) {
    return &pb.BearerTokenResponse{
        // your fake response...
    }, nil
}

If it's appropriate to get rid of the grpc client creation in the SubGenerateBearerToken method (e.g. keep it in the Subtype or pass as an arg) you could easily create a simple fake client:

type fakeClient struct {
    grpc.YourClient // a client interface from your *.pb.go file
}

func NewFakeClient(_ *grpc.ClientConn) *fakeClient {
    return &fakeClient{}
}

func (fc *fakeClient) GetBearerToken_grpc(ctx context.Context, in *pb.GetBearerTokenRequest, opts ...grpc.CallOption) (*pb.BearerTokenResponse, error) {
    return &pb.BearerTokenResponse{
        // your fake response...
    }, nil
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文