Mocking HTTP responses

Use an OpenAPI contract to mock HTTP responses to API calls.

Please enter the simulation

wiretap can be used as a full simulation of a real API. We call it mock mode.

To enable it, use the -x or the --mock-mode flags when running wiretap with an OpenAPI Specification.

wiretap -s giftshop-openapi.yaml -u https://api.pb33f.io -x

Try before you buy

mock mode simulates a real, working web API by using an OpenAPI contract to simulate the responses to API calls.

The simulation is based on the OpenAPI contract, which means the API will be contractually correct, ideal for testing and development. Developers can use the mock API to test their code, without having to wait for the real API to be built. mock mode also allows developers to test their code against different scenarios, such as error responses, or slow responses.

mock mode helps developers ensure a client is fully compliant with an API, before the API is even built.

Call it a client, call it a UI - whatever you call it, mock mode helps developers ensure a client is fully compliant with an API, before the API is built. The backend does not need to exist, as long as you have a valid OpenAPI specification, you can use mock mode to simulate the API.


But why?

API First / Design First development is a great way to build APIs. It’s a great way to build clients and UIs too. The API contract is designed first, taking in to consideration all the requirements of consumers of an API, by having the consumers involved in the design.

It allows us to answer questions upfront like:

  • Does the API contain all the data and information a client will need?
  • Are the data models designed so the client can use them easily?
  • Does the UI need to make 500 calls to extract a single page of data?
  • Has security, pagination, rate limiting, etc. been considered?
  • What happens when the API returns an error?

Answer all of these questions up front with a working API to play around with designs and ideas, without having to write a single line of backend code.

Avoid making costly mistakes to a production API, by trying out new designs and ideas in a mock API first.


Why use wiretap?

Similar functionality is available in other tools, such as Stoplight Prism. However, there are some advantages of using wiretap for mocking over other tools:

  • Much faster than Prism, and other tools.
  • Has more features, like static hosting, path rewriting the monitor UI and more.
  • Distributed as a small single binary, no dependencies, no runtime.
  • Lightweight, uses less memory and CPU than Prism, and it’s only 22mb.

How does it work?

When enabling mock mode, wiretap will use the OpenAPI contract to simulate the responses to API calls, before it does this however, it will validate the request against the contract, and if the request is invalid, it will return an error response.

wiretap will also try to locate an appropriate error message in the OpenAPI contract, and return that, otherwise it will return a wiretap specific error message that contains details of what went wrong.

Here is a high-level flow of the logic used to decide what do to with an incoming request, when in mock mode.

Flow diagram of wiretap's mock engine logic.
High level overview of wiretap’s internal mock engine handles requests.

Any error that is output in a red box, is an error generated by wiretap itself, and not the OpenAPI contract. When there is no representation of that error in the OpenAPI contract, wiretap will return an RFC7807 compliant error response that has been generated with a relevant HTTP code and a wiretap specific error message.

A good way to find gaps in an API contract, is to have wiretap generate errors for you. It may get messy!


Multiple examples

When a suitable response is found, and there are multiple examples for that response, wiretap will select the first one it finds, which could be any of them, but it’s generally the first one loaded in from the spec.

Preferred examples

If a preferred example is desired for a specific API response, wiretap can be informed to select a specific example by name using the Preferred header.

For example if we have a MediaType with two different examples defined something like this:

  examples:
    NewProductExample:
      summary: "A product to create"
      value:
        name: "pb33f t-shirt"
        description: "A t-shirt with the pb33f logo on it."
        price: 19.99
        category: "shirts"
        image: "https://pb33f.io/images/t-shirt.png"
    SomeOtherExample:
      summary: "Another example"
      value:
        name: "pb33f hat"
        description: "A baseball cap with the pb33f logo on it."
        price: 29.99
        category: "hats"
        image: "https://pb33f.io/images/hat.png"

And we want to extract the SomeOtherExample example, we can use the Preferred header to tell wiretap to use that example.

curl "https://localhost:9090/some/api/endpoint" -H "Preferred: SomeOtherExample"

Override response code

If you want to override the response code that wiretap will return, set a header named wiretap-status-code with the HTTP response code you want to return.

The return code of your mock will then reflect this value and override what ever is defined by the OpenAPI specification.

This will allow you to test how your client handles different response codes with successful mock responses.

Only works when a successful mock response is generated. Available from wiretap version v0.1.11


Static mocking

These documents were created by Akash Singh as part of the v0.4.0 release of wiretap.

This feature allows static mocking of APIs in the wiretap service by defining mock definitions in JSON files. It enables the server to match incoming requests against predefined mock definitions and return corresponding mock responses. If no match is found, the request is forwarded to the wiretap’s httpRequestHandler for further processing.

Enable static mocking

To enable static mocking, you need to set the --static-mock-dir argument to a directory path when starting wiretap, or configure it in the wiretap configuration file.

wiretap --static-mock-dir /path/to/mocks

When this path is set, wiretap will expect mock definitions and response body JSON files in the following structure:

  • /path/to/mocks/mock-definitions/ — Contains the mock definition JSON files.
  • /path/to/mocks/body-jsons/ — Contains the response body JSON files.

The static mock service will start and load all the mock definitions found in /path/to/mocks/mock-definitions.

Mock definitions

Mock definitions are JSON objects or arrays of objects that define the request and response structure. Each JSON object should contain the following keys:

  • request — Specifies the conditions for the request.
  • response — Specifies the response that should be returned when the request matches the conditions.

Request definition

The request definition is parsed into the following go type:

type StaticMockDefinitionRequest struct {
	Method        string               `json:"method,omitempty"`
	UrlPath       string               `json:"urlPath,omitempty"`
	Host           string               `json:"host,omitempty"`
	Header        *map[string]any  `json:"header,omitempty"`
	Body           interface{}        `json:"body,omitempty"`
	QueryParams *map[string]any  `json:"queryParams,omitempty"`
}

Each field can use either a string or a regex string to match the actual request. For example, the header, body, and queryParams fields can contain regex patterns to match the incoming request.

Example Request Definition:

{
	"method": "GET",
	"urlPath": "/test",
	"header": {
		"Content-Type": "application.*"
	},
	"queryParams": {
		"test": "ok",
		"arr": ["1", "2"]
	},
	"body": {
		"test": "o.*"
	}
}

Response definition

The response definition is parsed into the following go type:

type StaticMockDefinitionResponse struct {
	Header                    map[string]any      `json:"header,omitempty"`
	StatusCode              int                      `json:"statusCode,omitempty"`
	Body                       string                  `json:"body,omitempty"`
	BodyJsonFilename     string                  `json:"bodyJsonFilename,omitempty"`
}
  • BodyJsonFilename: The name of a file in the body-jsons folder, which contains the response body JSON. If this is specified, wiretap will return the content of that file instead of using the body field.

Example response definition with inline body:

{
	"statusCode": 200,
	"header": {
		"something-header": "test-ok"
	},
	"body": "{\"test\": \"${queryParams.arr.[1]}\"}"
}

In this example, the response body uses a reference to the request’s query parameters.

Example response definition with body from file:

{
	"statusCode": 200,
	"header": {
		"something-header": "test-ok"
	},
	"bodyJsonFilename": "test.json"
}

In this example, wiretap will look for a file named test.json in the body-jsons folder and return its content as the response body.

Response generation from requests

The response body can dynamically generate values based on the request. This is done by using the request’s fields (such as queryParams, body, etc.) in the response body.

For example:

{
	"statusCode": 200,
	"header": {
		"something-header": "test-ok"
	},
	"body": "{\"test\": \"${queryParams.arr.[1]}\"}"
}

In this case, the response body will include the second element from the arr query parameter in the incoming request. The ${} syntax is used to refer to the request’s fields.

Directory Structure

The --static-mock-dir should point to a directory that contains the following subdirectories and files:

/path/to/mocks/
  ├── mock-definitions/
  │     ├── mock1.json
  │     ├── mock2.json
  │     └── ...
  └── body-jsons/
        ├── test.json
        └── ...
  • mock-definitions/: Contains the mock request and response definitions.
  • body-jsons/: Contains the actual response body JSON files referenced by the mock definitions.

Example

  1. Directory Structure:
/mocks/
  ├── mock-definitions/
  │     ├── get-test-mock.json
  └── body-jsons/
        ├── test.json
  1. get-test-mock.json:
{
  "request": {
    "method": "GET",
    "urlPath": "/test",
    "header": {
      "Content-Type": "application.*"
    },
    "queryParams": {
      "test": "ok",
      "arr": ["1", "2"]
    },
    "body": {
      "test": "o.*"
    }
  },
  "response": {
    "statusCode": 200,
    "header": {
      "something-header": "test-ok"
    },
    "bodyJsonFilename": "test.json",
  }
}
  1. test.json:
{
  "test": "${queryParams.arr.[1]}"
}

With this configuration, when wiretap receives a GET request to /test, it will respond with the content of test.json.

Notes

  • If no mock definition is found that matches an incoming request, wiretap will forward the request to the wiretap’s request handler and let it return a response.
  • The mock definitions can contain either a single object or an array of objects. In the case of an array, each object represents a separate mock definition.