-
Notifications
You must be signed in to change notification settings - Fork 11
Getting Started
protoc-gen-mock
is a plugin to generate gRPC mock services based on protobuf specifications. The generated mocks can be used for:
- Mocking a dependent service for testing purposes
- Stub a dependent service during development (while the real service is not implemented yet)
It is required to have protoc-gen-go
installed. Run the following to install it:
go install github.com/golang/protobuf/protoc-gen-go
Then install protoc-gen-mock
by running:
go install github.com/carvalhorr/protoc-gen-mock
Suppose you have a proto definition called greeter.proto
with the content:
syntax = "proto3";
package carvalhorr.greeter;
option go_package = ".;greeter_service";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
Run the command below to generate the mock.
protoc --plugin protoc-gen-mock --go_out=plugins=grpc:greeter-service --mock_out=greeter-service greeter.proto
The command above will generate the files:
greeter-service
├── Dockerfile
├── greeter.mock.pb.go
├── greeter.pb.go
└── main.go
Build the docker image by issuing the command below from the greeter-service
directory:
docker build -t greeter:1.0 .
Now you can start the mock service in the default port using:
docker run -p 10010:10010 -p 1068:1068 greeter:1.0
By default, the mock service is started on the port 10010
, and the management API is listening on the port 1068
.
You can override the default ports by using:
docker run -e REST_PORT=1069 -e GRPC_PORT=50051 -p 10010:50051 -p 1010:1069 tag-version
If you wish you can customize the start of the mock service. See example:
Create a file called greeter.go
with the content:
package main
import (
"github.com/carvalhorr/protoc-gen-mock/bootstrap"
"github.com/carvalhorr/protoc-gen-mock/grpchandler"
"github.com/carvalhorr/protoc-gen-mock/stub"
greetermock "greeter-service" // Import the generated mock service
)
func main() {
bootstrap.BootstrapServers("./tmp/", 1068, 10010, MockServicesRegistrationCallback)
}
var MockServicesRegistrationCallback = func(stubsMatcher stub.StubsMatcher) grpchandler.MockService {
return grpchandler.NewCompositeMockService([]grpchandler.MockService{
NewGreeterMockService(stubsMatcher),
// You can start multiple mock services at once.
// Just call the generated constructor to the other mock services here.
})
// Alternatively
// return NewGreeterMockService(stubsMatcher)
}
Run the mock service:
go build
./greeter
You will see the message in the console:
INFO[2020-04-26T18:13:35+01:00] Supported methods: /carvalhorr.greeter.Greeter/Hello
INFO[2020-04-26T18:13:35+01:00] REST Server listening on port: 1068
INFO[2020-04-26T18:13:35+01:00] gRPC Server listening on port: 10010
The mock service is listening in two different ports:
- 1068 - A rest service to add/delete/update/get stubs
- 10010 - The gRPC mocked service
You may need to include the -trimpath
parameter to the build command if you are using advanced error mocking. In that case, build the program using:
go build -trimpath
./greeter
Please refer to Advanced Error Mocking for more details.
With the mock service running, you are ready to create the stubs you want the mock service to be able to respond. You can do it programmatically by using the generated client for managing stubs. Alternatively, you can do it using Postman, curl or any other REST client.
import greetservice ... // the generated mock service
func TestSomething {
...
// Start the client pointing to the REST API
m := greetservice.NewGreeterRemoteMockClient("127.0.0.1", 1068)
// Metadata can be used for deciding which response to return
md := metadata.Pairs("key1", "value1", "key2", "value2")
ctx := metadata.NewOutgoingContext(context.Background(), md)
// Mock a successfull response
m.OnSayHello(ctx, &greetservice.HelloRequest{Name: "Rodrigo"}).
Return(&greetservice.HelloReply{Message: "Hello, Rodrigo."})
...
// Delete existing mocks
m.Clear()
// Mock an error response
m.OnSayHello(ctx, &greetservice.HelloRequest{Name: "Carvalho"}).
Error(codes.NotFound, "Not found")
...
m.Clear()
}
Example:
POST 127.0.0.1:1068/stubs
{
"fullMethod": "/carvalhorr.greeter.Greeter/SayHello",
"request": {
"match": "exact",
"content": {
"name": "John"
}
},
"response": {
"type": "success",
"content": {
"message": "Hello, John"
}
}
}
You can verify the stubs that were created with:
GET 127.0.0.1:1068/stubs
Please refer to the Managing Stubs Using the REST API for more details about the REST API.
Use your gRPC client to connect to the mock server. By default, it runs on port 10010
and will respond as if it was the real service using the stubs you previously created.
If you created the stub above, now you can make a request to the gRPC method /carvalhorr.greeter.Greeter/SayHello
with the payload {"name": "John"}
and get the response {"message": "Hello, John"}
.