Extending WireMock
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 value) {
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
.