シナリオテスティングツール/パッケージであるrunnをgRPCに対応させた
gRPC、Unary以外のメソッドの挙動が面倒すぎてうまくrunnの実装の落とせない
— k1LoW (@k1LoW) 2022年6月26日
特にBidirectional-Streaming、お前だ
なんとかgRPC対応ができました。
「そもそもrunnって何?」については会社のテックブログにエントリを書きましたのでそちらをご覧ください。
なお、runnを使ったgRPCのシナリオテストの実戦投入は私もまだなので、もし触ってくださる方がいましたら是非フィードバックをお待ちしています。
gRPC Runner
runnでシナリオの各ステップを実行するコンポーネントをRunnerと呼んでいるのですが、今回gRPC Runnerを追加しました。
runners:
セクションで grpc://
というschemeでgRPC Runnerを作成できるようにしています。
次の例だと greq
という名前をつけたgRPC Runnerを作成しています。
runners: greq: grpc://grpc.example.com:80
443ポートにするとTLSを使用するようになります。細かく設定する場合は以下のように書けます。
runners: greq: addr: grpc.example.com:8080 tls: true cacert: path/to/cacert.pem cert: path/to/cert.pem key: path/to/key.pem # skipVerify: false
私が主に想定しているユースケースが「開発しているgRPCサーバのシナリオテスト」なので、大抵は次のようなコードで runners:
セクションの設定を上書きします(次の例だと greq
を runn.Runner() で上書きしています)。
func TestServer(t *testing.T) { addr := "127.0.0.1:8080" l, err := net.Listen("tcp", addr) if err != nil { t.Fatal(err) } ts := grpc.NewServer() myapppb.RegisterMyappServiceServer(s, NewMyappServer()) reflection.Register(s) go func() { s.Serve(l) }() t.Cleanup(func() { ts.GracefulStop() }) opts := []runn.Option{ runn.T(t), runn.Runner("greq", fmt.Sprintf("grpc://%s", addr), } o, err := runn.Load("testdata/books/**/*.yml", opts...) if err != nil { t.Fatal(err) } if err := o.RunN(ctx); err != nil { t.Fatal(err) } }
gRPCリクエストの書き方
4種類の通信方式はそれぞれ次のようにかけます。
なお、protoファイルは https://github.com/k1LoW/runn/blob/main/testdata/grpctest.proto を想定しています。
Unary RPC
headers:
と message:` でリクエストを投げることができます。レスポンスはHTTP Runnerと同様に自動で記録され使用できます。
steps: hello_unary: desc: Request using Unary RPC greq: grpctest.GrpcTestService/Hello: headers: authentication: tokenhello message: name: alice num: 3 request_time: 2022-06-25T05:24:43.861872Z test: | steps.hello_unary.res.status == 0 && steps.hello_unary.res.message.num == 3
Server streaming RPC
Server streaming RPCもUnary RPCと同様に headers:
と message:
でリクエストを投げることができます。
レスポンスは複数のメッセージが返ってくることがあるため steps.<step_id>.res.messages
に配列でレスポンスメッセージが記録されます。
steps: hello_server_streaming: desc: Request using Server streaming RPC greq: grpctest.GrpcTestService/ListHello: headers: authentication: tokenlisthello message: name: bob num: 4 request_time: 2022-06-25T05:24:43.861872Z test: | steps.hello_server_streaming.res.status == 0 && len(steps.hello_server_streaming.res.messages) == 2 && steps.hello_server_streaming.res.messages[1].num == 34
Client streaming RPC
Client streaming RPCは複数のメッセージを送るので message:
の代わりに messages:
セクションで複数のリクエストを投げることができます。
steps: hello_client_streaming: desc: Request using Client streaming RPC greq: grpctest.GrpcTestService/MultiHello: headers: authentication: tokenmultihello messages: - name: alice num: 5 request_time: 2022-06-25T05:24:43.861872Z - name: bob num: 6 request_time: 2022-06-25T05:24:43.861872Z test: | steps.hello_client_streaming.res.status == 0 && steps.hello_client_streaming.res.message.num == 35
Bidirectional streaming RPC
同期的にではありますがBidirectional streaming RPCもサポートしています。
messages:
セクションで receive
キーワードでサーバからのメッセージを1つ受信し、close
キーワードで通信を閉じます。
hello_bidi_streaming: desc: Request using Bidirectional streaming RPC greq: grpctest.GrpcTestService/HelloChat: headers: authentication: tokenhellochat messages: - name: alice num: 7 request_time: 2022-06-25T05:24:43.861872Z - recieve # recieve server message - name: bob num: 8 request_time: 2022-06-25T05:24:43.861872Z - name: charlie num: 9 request_time: 2022-06-25T05:24:43.861872Z - close # close connection test: | steps.hello_bidi_streaming.res.status == 0 && len(steps.hello_bidi_streaming.res.messages) == 1
gRPCサポートを終えて
まだ実戦投入していないのでアレですが、これでrunnで最低限欲しいと思っていた機能がひと通り揃いました。
あとはテックブログに書いたような機能を必要性やモチベーションに応じて追加したり、細かいところをちまちまと整備したりしようかと思います*1。
そう言えば、gRPCのテストを書くために grpcstub を作ったりしたのですが、それはまたどこかで紹介したいです。
なお、runnを使ったgRPCのシナリオテストの実戦投入は私もまだなので、もし触ってくださる方がいましたら是非フィードバックをお待ちしています(大事なことなので2回(ry)。