Using WireMock in an async environment
Building Event Driven systems is great, but not all systems are Event Driven. Communication with those systems can be via HTTP and those systems may not be able to respond quickly to a request, taking up to minutes to serve a response.
Are there tools to mock such a situation to be used for testing?
Context
When a system takes up to minutes to respond to a request, it is possible to first respond technically to the request and later respond functionally.
The other system can POST
the functional response.
Then the response is asynchronously served.
WireMock to the rescue
WireMock has a PostServeAction
, but the documentation on it is limited, which made me write this blog, so everyone can happily use this to their extend.
You can add behaviour that runs after a response has been completely served by extending PostServeAction
and registering as an extension.
http://wiremock.org/docs/
Using WireMock’s PostServeAction
and extending WireMock’s Webhooks extension makes it possible to return the functional response.
How To
First, let’s extend WireMock’s Webhooks extension and make it known to WireMock.
In the request a messageId
is present and the response has a replyToMessageId
, so the response can be correlated to the request. This means the response needs to contain the request’s messageId
.
Define a Definition, containing a ResponseModel
and a serverPort
. The serverPort
is used to let the mock know on which port the server expecting the response, runs.
public class ResponseDefinition {
ResponseModel update;
Integer serverPort;
public static ResponseDefinition definition() {
return new ResponseDefinition();
}
}
The ResponseWebhook
extracts the messageId
from the request, extracts the definition, the ResponseModel
, and populates the replyToMessageId
with the extracted messageId
from the request.
Next we wrap this all in a new Webhooks
instance and call super.doAction()
. This will POST
the ResponseModel
to the configured endpoint.
public class ResponseWebhook extends Webhooks {
@Override
public String getName() {
return "ResponseWebhook";
}
@Override
public void doAction(ServeEvent serveEvent, Admin admin, Parameters parameters) {
var messageId = Json.node(serveEvent.getRequest().getBodyAsString()).get("messageId").asText();
var definition = parameters.as(ResponseDefinition.class);
var transformedUpdate = definition.getUpdate().withReplyToMessageId(messageId);
var webhookParameters = Webhooks.webhook()
.withMethod(RequestMethod.POST)
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withUrl("http://localhost:"+definition.getLocalServerPort()+"/i-want-the-response")
.withBody(Json.write(transformedUpdate));
super.doAction(serveEvent, admin, Parameters.of(webhookParameters));
}
}
The next step is to register the extension.
var wireMockServer = new WireMockServer(
options()
.dynamicPort()
.extensions(
ResponseWebhook.class
)
);
wireMockServer.start();
At last create a mock using the ResponseWebhook
extension.
givenThat(post(urlPathEqualTo("/other-domain"))
.willReturn(aResponse().withStatus(202))
.withPostServeAction(
"ResponseWebhook",
ResponseWebhook.definition()
.withLocalServerPort(localServerPort)
.withUpdate(new ResponseModel())
);
The code above returns a HTTP-202 (ACCEPTED)
, the technical response, and will execute the PostServeAction
. The PostServeAction
will send the functional response to an endpoint.