grpc-wiremock
Overview
grpc-wiremock is a mock server for GRPC services implemented as a wrapper around the WireMock http server.
How It Works
grpc-wiremock starts a gRPC server generated based on provided proto files which will convert a proto grpc request to JSON and redirects it as a POST request to the WireMock then converts a http response back to grpc proto format.
- GRPC server works on
tcp://localhost:50000
- WireMock server works on
http://localhost:8888
Quick Usage
- Run
docker run -p 8888:8888 -p 50000:50000 -v $(pwd)/example/proto:/proto -v $(pwd)/example/wiremock:/wiremock adven27/grpc-wiremock
- Stub
curl -X POST http://localhost:8888/__admin/mappings \ -d '{ "request": { "method": "POST", "url": "/BalanceService/getUserBalance", "headers": {"withAmount": {"matches": "\\d+\\.?\\d*"} }, "bodyPatterns" : [ { "equalToJson" : { "userId": "1", "currency": "EUR" } } ] }, "response": { "status": 200, "jsonBody": { "balance": { "amount": { "value": { "decimal" : "{{request.headers.withAmount}}" }, "value_present": true }, "currency": { "value": "EUR", "value_present": true } } } }}'
- Check
grpcurl -H 'withAmount: 100.0' -plaintext -d '{"user_id": 1, "currency": "EUR"}' localhost:50000 api.wallet.BalanceService/getUserBalance
Should get response:
{ "balance": { "amount": { "value": { "decimal": "100.0" }, "value_present": true }, "currency": { "value": "EUR", "value_present": true } }}
Stubbing
Stubbing should be done via WireMock JSON API
Error mapping
Default error (not 200 OK
) mapping is based on https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto :
HTTP Status Code | GRPC Status |
---|---|
400 Bad Request | INVALID_ARGUMENT |
401 Unauthorized | UNAUTHENTICATED |
403 Forbidden | PERMISSION_DENIED |
404 Not Found | NOT_FOUND |
409 Conflict | ALREADY_EXISTS |
429 Too Many Requests | RESOURCE_EXHAUSTED |
499 Client Closed Request | CANCELLED |
500 Internal Server Error | INTERNAL |
501 Not Implemented | UNIMPLEMENTED |
503 Service Unavailable | UNAVAILABLE |
504 Gateway Timeout | DEADLINE_EXCEEDED |
And could be overridden or augmented by overriding or augmenting the following properties:
grpc: error-code-by: http: status-code: 400: INVALID_ARGUMENT 401: UNAUTHENTICATED 403: PERMISSION_DENIED 404: NOT_FOUND 409: ALREADY_EXISTS 429: RESOURCE_EXHAUSTED 499: CANCELLED 500: INTERNAL 501: UNIMPLEMENTED 503: UNAVAILABLE 504: DEADLINE_EXCEEDED
For example:
docker run \ -e GRPC_ERRORCODEBY_HTTP_STATUSCODE_400=OUT_OF_RANGE \ -e GRPC_ERRORCODEBY_HTTP_STATUSCODE_510=DATA_LOSS \ adven27/grpc-wiremock
How To:
1. Configure gRPC server
Currently, following grpc server properties are supported:
GRPC_SERVER_PORTGRPC_SERVER_MAXHEADERLISTSIZEGRPC_SERVER_MAXMESSAGESIZEGRPC_SERVER_MAXINBOUNDMETADATASIZEGRPC_SERVER_MAXINBOUNDMESSAGESIZE
Could be used like this:
docker run -e GRPC_SERVER_MAXHEADERLISTSIZE=1000 adven27/grpc-wiremock
2. Configure WireMock server
WireMock server may be configured by passing command line options
prefixed by wiremock_
:
docker run -e WIREMOCK_DISABLE-REQUEST-LOGGING -e WIREMOCK_PORT=0 adven27/grpc-wiremock
3. Mock server-side streaming:
Given the service:
service WalletService { rpc searchTransaction (SearchTransactionRequest) returns (stream SearchTransactionResponse) {}}
Then the following stub may be provided, where response.headers.streamSize
specifies
how many responses should be returned during the stream (1
- if absent).
The current response iteration number is available in request.headers.streamCursor
:
curl -X POST http://localhost:8888/__admin/mappings \ -d '{ "request": { "method": "POST", "url": "/WalletService/searchTransaction" }, "response": { "fixedDelayMilliseconds": 1000, "headers": {"streamSize": "5" }, "jsonBody": { "transactions": [ { "id": "{{request.headers.streamCursor}}", "userId": "1", "currency": "EUR", "amount": { "decimal": "{{request.headers.streamCursor}}00" } }, { "id": "100{{request.headers.streamCursor}}", "userId": "2", "currency": "EUR", "amount": { "decimal": "200" } } ] } }}'
4. Speed up container start
In case you don't need to change proto files, you can build your own image with precompiled protos.
See an example
5. Use with snappy compresser/decompresser
Snappy support can be enabled using EXTERNAL_CODECS
env variable as follows:
docker run -e EXTERNAL_CODECS="snappy, another" adven27/grpc-wiremock
Also in docker-compose:
image: adven27/grpc-wiremock ports: - "12085:50000" # grpc port - "8088:8888" # http serve port volumes: - ./example/proto:/proto environment: - EXTERNAL_CODECS=snappy
*gzip compression supported by default
6. Use in load testing
To increase performance some Wiremock related options may be tuned either directly or by enabling the "load" profile. Next two commands are identical:
docker run -e SPRING_PROFILES_ACTIVE=load adven27/grpc-wiremock
docker run \ -e WIREMOCK_NO-REQUEST-JOURNAL \ -e WIREMOCK_DISABLE-REQUEST-LOGGING \ -e WIREMOCK_ASYNC-RESPONSE-ENABLED \ -e WIREMOCK_ASYNC-RESPONSE-THREADS=10 \ adven27/grpc-wiremock
7. Preserving proto field names in stubs
By default, stub mappings must have proto fields references in lowerCamlCase, e.g. proto field user_id
must be referenced as:
{ "request": { "method": "POST", "url": "/BalanceService/getUserBalance", "bodyPatterns": [{"equalToJson": { "userId": "1" }}] }}
To preserve proto field names the following env variable could be used:
docker run -e JSON_PRESERVING_PROTO_FIELD_NAMES=true adven27/grpc-wiremock