This is a protoc plugin which generates golang source code for gRPC scenario test (only Unary). The plugin can test the gRPC methods defined in your .proto file. The necessary preparation is the source code that calls the test using your .proto file and the JSON file that defines the test scenario, and the simple gRPC service client and testing package.
To use this plugin, you need to use protoc-gen-go to generate Golang source code.
go get -u github.com/yoshd/protoc-gen-stest
protoc -I. --plugin=path/to/protoc-gen-stest --stest_out=. your.proto
See examples
- Suppose you have the following yoshd.proto:
syntax = "proto3";
option go_package = "pb";
service Yoshd {
rpc Yoshi (YoshiRequest) returns (YoshiResponse) {
}
}
message YoshiRequest {
string req_msg = 1;
}
message YoshiResponse {
string res_msg = 1;
}
- Generate source codes.
protoc -I. --plugin=path/to/protoc-gen-stest --go_out=plugins=grpc:pb --stest_out=pb your.proto
- The fields of JSON are as follows.
- For
action
, write gRPC method name. - For
request
, write request parameters. - For
expected_response
, write the value of the expected response. If you expect error response, you do not need to write it. - For
loop
, specify the number of times to repeat the request. Default1
- For
success_rule
, specify the rule for considering the test as successful. There are two kinds of rules as follows. Defaultall
all
: All the responses in theloop
must be responses as expected.once
: If the response is as expected even once in theloop
, the test is regarded as successful.
- For
sleep
, specify the number of seconds to sleep before sending the request. Default0
- For
error_expectation
, write whether or not to expect an error response. Defaultfalse
For expected_error_code
, write the expected gPRC error code as a numerical value.
- For
The field names of the request and response are the same as those of the JSON tag attached to the structure of the code generated by protoc-gen-go.
In this example, the first test will succeed if the expected response is returned at least once while looping Yoshi
twice. The first test sleeps for 3 seconds each time before calling Yoshi
.
In the second test, an error response is returned, and if the gRPC error code is 3 (InvalidArgument), the test succeeds.
Please refer to codes for the error code of gPRC.
[
{
"action": "Yoshi",
"request": {
"req_msg": "Yoshi!"
},
"expected_response": {
"res_msg": "YoshiYoshi!"
},
"loop": 2,
"sleep": 3,
"success_rule": "once"
},
{
"action": "Yoshi",
"request": {
"req_msg": "Yoshi"
},
"error_expectation": true,
"expected_error_code": 3
}
]
- Write gRPC client, code to compare expected response and actual response, test call in Golang.
- The default behavior is to compare expected response and actual response with
reflect.DeepEqual
- The default behavior is to compare expected response and actual response with
package examples
import (
"testing"
"github.com/yoshd/protoc-gen-stest/examples/pb"
"google.golang.org/grpc"
)
func TestScenario(t *testing.T) {
target := "localhost:13009"
client, _ := grpc.Dial(target, grpc.WithInsecure())
defer client.Close()
yoshd := pb.NewYoshdClient(client)
testClient := pb.NewTestClient(yoshd)
testClient.RunGRPCTest(
t,
"path/to/yoshd.json",
nil,
)
}
- If you want to specify how you want to compare the expected response to the actual response, you need the code on how to compare the responses. The function must accept the following arguments and return an error.
func(expectedResponse, response interface{}) error
- Since it is
interface
, we need to cast it to the response type of each gPRC method and compare it.
- Since it is
- In the
compareFuncMap
argument ofRunGRPCTest
, specify the gPRC method name in key and put the above function in value.
package examples
import (
"errors"
"testing"
"github.com/yoshd/protoc-gen-stest/examples/pb"
"google.golang.org/grpc"
)
var compareFuncMap = map[string]*func(expectedResponse, response interface{}) error{}
func TestScenario(t *testing.T) {
target := "localhost:13009"
client, _ := grpc.Dial(target, grpc.WithInsecure())
defer client.Close()
yoshd := pb.NewYoshdClient(client)
testClient := pb.NewTestClient(yoshd)
testClient.RunGRPCTest(
t,
"path/to/yoshd.json",
compareFuncMap,
)
}
func setUp() {
// How to compare the expected response with the actual response and return an error
YoshiCompareFunc := func(expectedResponse, response interface{}) error {
if expectedResponse == nil || response == nil {
return nil
}
// Requires cast from interface
er := expectedResponse.(pb.YoshiResponse)
r := response.(pb.YoshiResponse)
if er.ResMsg != r.ResMsg {
return errors.New("The actual response of the Yoshi was not equal to the expected response")
}
}
// Specify the gPRC method name as key and put a function to compare the response to value.
compareFuncMap["Yoshi"] = &YoshiCompareFunc
}
func TestMain(m *testing.M) {
setUp()
m.Run()
}
- Run the test
go test -v yoshd_test.go