/grpc-wiremock

gRPC Mock Server

Primary LanguageJavaMIT LicenseMIT

Stability: Maintenance Docker Cloud Build Status Docker Image Version (tag latest semver)

Overview

grpc-wiremock is a mock server for GRPC services implemented as a wrapper around the WireMock http server.

How It Works

Overview 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.

  1. GRPC server works on tcp://localhost:50000
  2. WireMock server works on http://localhost:8888

Quick Usage

  1. Run
docker run -p 8888:8888 -p 50000:50000 -v $(pwd)/example/proto:/proto -v $(pwd)/example/wiremock:/wiremock adven27/grpc-wiremock
  1. 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 }
            } 
        }
    }
}'
  1. Check
grpcurl -H 'withAmount: 100.0' -plaintext -d '{"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_PORT
GRPC_SERVER_MAXHEADERLISTSIZE
GRPC_SERVER_MAXMESSAGESIZE
GRPC_SERVER_MAXINBOUNDMETADATASIZE
GRPC_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