Cameron Hotchkies

Categories

  • testing

Tags

  • continuous-integration
  • gitlab
  • mocking
  • testing

In a healthy project there will be a lot of tests. One of the tools that makes for convenient testing is WireMock. If you are not already familiar, WireMock is a service that listens for HTTP requests matching certain conditions and replies with canned responses.

Change control for requests

One of the features I like about WireMock is that the canned responses can be defined in individual JSON files, loaded at startup. This is convenient for tracking the mocks via source control, as well as making them portable between projects of differing source languages.

Here is a sample that defines the request for a fictional status endpoint. The response can also be defined inline, but if it is non-trivial, or binary, I prefer to store that in an additional file, which is very easy to do.

{
  "request": {
    "method": "GET",
    "urlPath": "/api/status",
    "queryParameters" : {
      "withDetails": {
        "format": "json"
      }
    }
  },
  "response": {
    "bodyFileName" : "sample_status_response.json"
  }
}

Using WireMock in practice

For local development, it’s very easy to set up run a WireMock docker image. Frequently, images will expect the stub mapping files to be mounted in the image. With docker-compose this is a trivially easy process. (The semisafe image passes all docker image arguments directly to the WireMock binary)

mock-api-x:
  image: "semisafe/wiremock:latest"
  command: "--root-dir /mocks"
  volumes:
    - ./mocks/api_x:/mocks
  ports:
    - "8081:8080"

At this point there is now a local server listening for incoming connections. Any unit tests that connect to localhost:8081 with a request matching the defined stub will get the defined response in return.

Integrating into the build pipeline

An important part of any build pipeline is to ensure tests are run during the review cycle. In GitLab, having build pipelines with test coverage indicators is included in even the most basic plans. To provide support for ancillary dependent systems, GitLab CI provides services. The documentation for this feature is a little sparse. It looks similar to docker-compose because it’s yaml based, and relates to docker images. In reality, the feature set is not similar at all.

Mounting volumes into service containers is not reliably supported with services in the Gitlab Runner. After trying to hunt down a solution to mount the mapping files from the repository under test, it finally occurred to me that I was taking the wrong approach. I have already been using Flyway for dynamic schema application for quite some time. WireMock also has an Admin API that allows for stubbed requests to be added dynamically.

This led me to throw together a small tool to automate the process of taking the mapping files from a local filesystem and posting them to the Admin API.

This can be run manually with the following:

yarn start -m /path/to/mocks -h mock-api-x -p 8081

Complete the build image

Alternatively, wiremock-loader can be embedded in the base Docker image used for the CI build process (here is the one I use for Scala / Play projects that also includes Flyway). Once it has been added to the base build image, each service can be initialized before running the tests:

build:
  stage: build
  services:
    - name: "semisafe/wiremock:latest"
      alias: fake-api-x
  script:
    # If you have a DB, now would be a good
    # time to call Flyway migrations

    # Load the WireMock service
    - wiremock-loader -m ./mocks/api_x -h fake-api-x -p 8080

    # Execute the actual build / test-runner
    - sbt clean coverage test coverageReport

This can now be used to create a separate WireMock instance for each required dependency. While it could be easier to combine all dependencies into the same WireMock instance, that could hide subtle issues where hostnames get mangled or confused in the configuration.