LoginSignup
22
14

More than 3 years have passed since last update.

[gRPC-Go] Serverのテストをネットワーク接続なしで行う

Posted at

gRPC Serverのテスト

gRPCサーバーは通常はTCPサーバーを立てる必要があり、テストしようとすると面倒です。

そこで、通信部分をin-memoryに差し替えてくれるgrpc/test/bufconnというパッケージを利用すると便利です。

bufconnはネットワークサーバーのように振る舞うListenerを持ち、さらにDial()メソッドでクライアント接続のように振る舞うnet.Connも持つという特徴があります。

bufconnを使ってネットワーク接続なしでテスト

それでは、実際にbufconnを使ったテストを書いてみます。

ソースコードは次のリポジトリにも公開しています。
https://github.com/castaneai/grpc-testing-with-bufconn

たとえばgRPCの公式チュートリアルにもある次のようなシンプルなRPCで考えます。

syntax = "proto3";

package hello;

service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
    string name = 1;
}

message HelloReply {
    string message = 1;
}

protoc でGoの定義ファイルを生成して、次のようなディレクトリ構成とします。

$ protoc greeter.proto --go_out=plugins=grpc:.
.
├── go.mod
├── go.sum
├── greeter.pb.go
├── greeter.proto
└── server
   ├── server.go
   └── server_test.go

server/ ディレクトリ以下に2つのファイルを作ります。

  • server.go サーバーの実装
  • server_test.go サーバーのテスト(bufconnを使う部分)

server.go何も特別なことはなく、通常のgRPCサーバーの実装です。

次に、 server_test.go ですがここが重要です!

gRPC-Goではgrpc.Dial()でサーバーへ接続をしますが、そこで grpc.DialContext(grpc.WithCotnextDialer(...)) を使います。

WithContextDialerの中身をbufconnへの接続 lis.Dial() に差し替えるというやり方です。

server_test.go
package main

import (
    "context"
    "log"
    "net"
    "testing"

    "google.golang.org/grpc"
    "google.golang.org/grpc/test/bufconn"

    pb "github.com/castaneai/grpc-testing-with-bufconn"
)

const bufSize = 1024 * 1024

var lis *bufconn.Listener

func init() {
    lis = bufconn.Listen(bufSize)
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    go func() {
        if err := s.Serve(lis); err != nil {
            log.Fatal(err)
        }
    }()
}

func bufDialer(ctx context.Context, address string) (net.Conn, error) {
    return lis.Dial()
}

func TestSayHello(t *testing.T) {
    ctx := context.Background()
    conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure())
    if err != nil {
        t.Fatal(err)
    }
    defer conn.Close()

    client := pb.NewGreeterClient(conn)
    resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "test"})
    if err != nil {
        t.Fatal(err)
    }

    if resp.GetMessage() != "Hello test" {
        t.Fatal("hello reply must be 'Hello test'")
    }
}

これでテストを実行してみると、即座にテストが通りました! :smile:

$ go test -v ./...
=== RUN   TestSayHello
2019/10/04 12:50:04 Received: test
--- PASS: TestSayHello (0.00s)
PASS
ok      github.com/castaneai/grpc-testing-with-bufconn/server   0.024s
22
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
14