Skip to content

Getting Started

Rodrigo edited this page Mar 6, 2021 · 6 revisions

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)

Installation

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

Creating mock services

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

Starting the Mock Server

Docker

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

Customized Startup

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

Starting the Mock Server with Support for Advanced Error Mocking

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.

Stubs

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.

Mocking Using the Generated 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()
}

Mocking Using the REST API

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.

Using the Mock Server

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"}.

Clone this wiki locally