Extending WireMock

WARNING: This document is for an old WireMock 2.x baseline. See the documentation for the current WireMock 3.x baseline here

Registering Extensions #

You can register the extension programmatically via its class name, class or an instance:

new WireMockServer(wireMockConfig()
  .extensions("com.mycorp.BodyContentTransformer", "com.mycorp.HeaderMangler"));

new WireMockServer(wireMockConfig()
  .extensions(BodyContentTransformer.class, HeaderMangler.class));

new WireMockServer(wireMockConfig()
  .extensions(new BodyContentTransformer(), new HeaderMangler()));

See Running as a Standalone Process for details on running with extensions from the command line.

Transforming Responses #

Sometimes, returning wholly static responses to stub requests isn’t practical e.g. when there are transaction IDs being passed between request/responses, dates that must be current. Via WireMock’s extension mechanism it is possible to dynamically modify responses, allowing header re-writing and templated responses amongst other things.

There are two ways to dynamically transform output from WireMock. WireMock stub mappings consist in part of a ResponseDefinition. This is essentially a description that WireMock (sometimes) combines with other information when producing the final response. A basic ResponseDefinition closely resembles an actual response in that it has status, headers and body fields, but it can also indicate to WireMock that the actual response should be the result of a proxy request to another system or a fault of some kind.

ResponseDefinition objects are “rendered” by WireMock into a Response, and it is possible to interject either before or after this process when writing an extension, meaning you can either transform the ResponseDefinition prior to rendering, or the rendered Response afterwards.

Parameters #

Transformer extensions can have parameters passed to them on a per-stub basis via a Parameters object passed to their primary method. Parameters derives from Java’s Map and can be therefore arbitrarily deeply nested. Only types compatible with JSON (strings, numbers, booleans, maps and lists) can be used.

Response definition transformation #

To transform ResponseDefinition, extend the ResponseDefinitionTransformer class:

public static class ExampleTransformer extends ResponseDefinitionTransformer {

        @Override
        public ResponseDefinition transform(Request request, ResponseDefinition responseDefinition, FileSource files, Parameters parameters) {
            return new ResponseDefinitionBuilder()
                    .withHeader("MyHeader", "Transformed")
                    .withStatus(200)
                    .withBody("Transformed body")
                    .build();
        }

        @Override
        public String getName() {
            return "example";
        }
    }

Transformer classes must have a no-args constructor unless you only intend to register them via an instance as described below.

Supplying parameters #

Parameters are supplied on a per stub mapping basis:

stubFor(get(urlEqualTo("/transform")).willReturn(
        aResponse()
                .withTransformerParameter("newValue", 66)
                .withTransformerParameter("inner", ImmutableMap.of("thing", "value")))); // ImmutableMap is from Guava, but any Map will do

or:

{
    "request": {
        "url": "/transform",
        "method": "GET"
    },
    "response": {
        "status": 200,
        "transformerParameters": {
            "newValue": 66,
            "inner": {
                "thing": "value"
            }
        }
    }
}

Non-global transformations #

By default transformations will be applied globally. If you only want them to apply in certain cases you can refer to make them non-global by adding this to your transformer class:

@Override
public boolean applyGlobally() {
    return false;
}

Then you add the transformation to specific stubs via its name:

stubFor(get(urlEqualTo("/local-transform")).willReturn(aResponse()
        .withStatus(200)
        .withBody("Original body")
        .withTransformers("my-transformer", "other-transformer")));

or:

{
    "request": {
        "method": "GET",
        "url": "/local-transform"
    },
    "response": {
        "status": 200,
        "body": "Original body",
        "transformers": ["my-transformer", "other-transformer"]
    }
}

The Java API also has a convenience method for adding transformers and parameters in one call:

stubFor(get(urlEqualTo("/transform")).willReturn(
        aResponse()
                .withTransformer("body-transformer", "newValue", 66)));

Response transformation #

A response transformer extension class is identical to ResponseDefinitionTransformer with the exception that it takes a Response in its transform method’s parameter list and returns a Response.

public static class StubResponseTransformerWithParams extends ResponseTransformer {

        @Override
        public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
            return Response.Builder.like(response)
                    .but().body(parameters.getString("name") + ", "
                            + parameters.getInt("number") + ", "
                            + parameters.getBoolean("flag"))
                    .build();
        }

        @Override
        public String getName() {
            return "stub-transformer-with-params";
        }
}

Custom Request Matchers #

If WireMock’s standard set of request matching strategies isn’t sufficient, you can register one or more request matcher classes containing your own logic.

Custom matchers can be attached directly to stubs via the Java API when using the local admin interface (by calling stubFor(...) on WireMockServer or WireMockRule). They can also be added via the extension mechanism and used with individual stubs by referring to them by name as described above for response transformers.

As with response transformers, per stub mapping parameters can be passed to matchers.

To add a matcher directly to a stub mapping:

wireMockServer.stubFor(requestMatching(new RequestMatcherExtension() {
    @Override
    public MatchResult match(Request request, Parameters parameters) {
        return MatchResult.of(request.getBody().length > 2048);
    }
}).willReturn(aResponse().withStatus(422)));

To use it in a verification :

verify(2, requestMadeFor(new ValueMatcher<Request>() {
    @Override
    public MatchResult match(Request request) {
        return MatchResult.of(request.getBody().length > 2048);
    }
}));

In Java 8 and above this can be achieved using a lambda:

wireMockServer.stubFor(requestMatching(request ->
    MatchResult.of(request.getBody().length > 2048)
).willReturn(aResponse().withStatus(422)));

To create a matcher to be referred to by name, create a class extending RequestMatcher and register it as an extension as per the instructions at the top of this page e.g.

public class BodyLengthMatcher extends RequestMatcherExtension {

    @Override
    public String getName() {
        return "body-too-long";
    }

    @Override
    public MatchResult match(Request request, Parameters parameters) {
        int maxLength = parameters.getInt("maxLength");
        return MatchResult.of(request.getBody().length > maxLength);
    }
}

Then define a stub with it:

stubFor(requestMatching("body-too-long", Parameters.one("maxLength", 2048))
        .willReturn(aResponse().withStatus(422)));

or via JSON:

{
    "request": {
        "customMatcher": {
            "name": "body-too-long",
            "parameters": {
                "maxLength": 2048
            }
        }
    },
    "response": {
        "status": 422
    }
}

Combining standard and custom request matchers #

An inline custom matcher can be used in combination with standard matchers in the following way:

stubFor(get(urlPathMatching("/the/.*/one"))
        .andMatching(new MyRequestMatcher()) // Will also accept a Java 8+ lambda
        .willReturn(ok()));

Note that inline matchers of this form can only be used from Java, and only when stubFor is being called against a local WireMock server. An exception will be thrown if attempting to use an inline custom matcher against a remote instance.

Custom matchers defined as extensions can also be combined with standard matchers.

Java:

stubFor(get(urlPathMatching("/the/.*/one"))
        .andMatching("path-contains-param", Parameters.one("path", "correct"))
        .willReturn(ok()));

JSON:

{
    "request": {
        "urlPathPattern": "/the/.*/one",
        "method": "GET",
        "customMatcher": {
            "name": "path-contains-param",
            "parameters": {
                "path": "correct"
            }
        }
    },
    "response": {
        "status": 200
    }
}

Post-serve actions #

You can add behaviour that runs after a response has been completely served by extending PostServeAction and registering as an extension (see above for details).

PostServeAction has two template methods either or both of which can be overridden depending on desired behaviour. To add per-stub behaviour override doAction(...). Overriding doGlobalAction(...) will add the behaviour globally.

Admin API extensions #

Additional API routes under WireMock’s /__admin endpoint can be configured by implementing AdminApiExtension.

Listening for requests #

If you’re using the JUnit rule or you’ve started WireMockServer programmatically, you can register listeners to be called when a request is received.

e.g. with the JUnit rule:

List<Request> requests = new ArrayList<Request>();
rule.addMockServiceRequestListener(new RequestListener() {
     @Override
     public void requestReceived(Request request, Response response) {
         requests.add(LoggedRequest.createFrom(request));
     }
});

for (Request request: requests) {
    assertThat(request.getUrl(), containsString("docId=92837592847"));
}

Listening for raw traffic #

If you would like to observe raw HTTP traffic to and from Jetty for debugging purposes you can use a WiremockNetworkTrafficListener.

One scenario where it can be useful is where Jetty alters the response from Wiremock before sending it to the client. (An example of that is where Jetty appends a –gzip postfix to the ETag response header if the response is gzipped.) Using a RequestListener in this case would not show those alterations.

To output all raw traffic to console use ConsoleNotifyingWiremockNetworkTrafficListener, for example:

new WireMockServer(wireMockConfig()
    .networkTrafficListener(new ConsoleNotifyingWiremockNetworkTrafficListener()));

If you would like to collect the traffic and for example add it to your acceptance test’s output, you can use the CollectingNetworkTrafficListener.

Intercepting and modifying requests #

Requests to both stubs and the admin API can be intercepted and either modified or halted with an immediate response. This supports a number of use cases including: authentication, URL rewriting and request header injection.

To intercept stub requests, create a class that extends StubRequestFilter. For instance, to perform simple authentication:

public class SimpleAuthRequestFilter extends StubRequestFilter {

    @Override
    public RequestFilterAction filter(Request request) {
        if (request.header("Authorization").firstValue().equals("Basic abc123")) {
            return RequestFilterAction.continueWith(request);
        }

        return RequestFilterAction.stopWith(ResponseDefinition.notAuthorised());
    }

    @Override
    public String getName() {
        return "simple-auth";
    }
}

Then add it as an extension as usual e.g.

new WireMockRule(wireMockConfig().extensions(new SimpleAuthRequestFilter()));

To intercept admin API follow the same process, but extend AdminRequestFilter.

Modifying the request during interception #

To modify the HTTP request, the simplest approach is to wrap the original request with a RequestWrapper then continue e.g.

public static class UrlAndHeadersModifyingFilter extends StubRequestFilter {

    @Override
    public RequestFilterAction filter(Request request) {
        Request wrappedRequest = RequestWrapper.create()
                .transformAbsoluteUrl(url -> url + "?extraQueryParam=123")
                .addHeader("X-Custom-Header", "headerval")
                .wrap(request);

        return RequestFilterAction.continueWith(wrappedRequest);
    }

    @Override
    public String getName() {
        return "url-and-header-modifier";
    }
}

RequestWrapper is a builder pattern and allows any number of changes to the request. It supports the following changes:

  • Transformation of the URL. transformAbsoluteUrl takes a FieldTransformer as a parameter (or equivalent lambda) which maps from the old to the new URL. Note that the URL passed in is absolute, and the returned URL must also be.
  • Addition, removal and transformation (via FieldTransformer) of headers.
  • Addition, removal and transformation of cookies.
  • Changing the HTTP method.
  • Transformation of the request body.
  • Transformation of body parts (if a multipart encoded request).