Shamefully I had a private fork of Wiremock at the company I worked for, we'd hacked away at it and added support for copying our platform headers, adding our HMAC signatures to responses etc. We'd also used it for load testing and made a bunch of the Jetty tuning options configurable. Some of this, HMAC, was confidential, 90% not so much :)
So over the Christmas holidays, with the help of Tom, I've been hacking away with Wiremock, and the new release now contains:
- Configurable number of container threads
- Exposed Jetty tuning options: Acceptor threads & Accept queue size
- Extension points
The first two were my PRs, the latter was by Tom, who (rightly) rejected my PR as it added too much latency to start up as it was reflection based. But kindly Tom hashed out an alternative documented here: https://github.com/tomakehurst/wiremock/issues/214
If you've used Wiremock before you'll know you run/interact it in two modes: via its Java API and as a standalone process. This means you can use it for unit/integration testing and black box acceptance testing. Let's look with the Java API, how to use this feature in standalone mode is documented on the Wiremock site:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public abstract class ResponseTransformer implements Extension { | |
public abstract ResponseDefinition transform(Request request, ResponseDefinition responseDefinition, FileSource files); | |
public boolean applyGlobally() { | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Override | |
public ResponseDefinition transform(Request request, ResponseDefinition responseDefinition, FileSource files) { | |
HttpHeaders stubbedResponseHeaders = responseDefinition.getHeaders(); | |
Collection<HttpHeader> responseHeaders = stubbedResponseHeaders == null ? new ArrayList<HttpHeader>() : stubbedResponseHeaders.all(); | |
Set<String> allHeaderKeys = request.getAllHeaderKeys(); | |
for (String headerName : allHeaderKeys) { | |
if (headerName.startsWith("Batey")) { | |
responseHeaders.add(new HttpHeader(headerName, request.getHeader(headerName))); | |
} | |
} | |
responseDefinition.setHeaders(new HttpHeaders(responseHeaders)); | |
return responseDefinition; | |
} | |
@Override | |
public String name() { | |
return "CopiesBateyHeaders"; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
stubFor(get(urlEqualTo("/batey-service")) | |
.willReturn(aResponse() | |
.withStatus(200) | |
.withBody("Original body") | |
.withTransformers("CopiesBateyHeaders"))); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public void callBatey() throws IOException { | |
HttpResponse httpResponse = Request.Get(host + "/batey-service") | |
.addHeader("Batey-Auth", "super-token") | |
.execute().returnResponse(); | |
Header authHeader = httpResponse.getFirstHeader("Batey-Auth"); | |
if (authHeader == null) { | |
throw new NoBateyAuthHeader(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Test | |
public void shouldForwardAuthHeaders() throws Exception { | |
//given | |
stubFor(get(urlEqualTo("/batey-service")) | |
.willReturn(aResponse() | |
.withStatus(200) | |
.withBody("Original body") | |
.withTransformers("CopiesBateyHeaders"))); | |
AwesomeHttpClient awesomeHttpClient = new AwesomeHttpClient("http://localhost:8089"); | |
//when | |
awesomeHttpClient.callBatey(); | |
//then | |
verify(getRequestedFor(urlEqualTo("/batey-service")) | |
.withHeader("Batey-Auth", equalTo("super-token"))); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Test(expected = NoBateyAuthHeader.class) | |
public void throwsExceptionIfNoAuthHeaderReturned() throws Exception { | |
//given | |
stubFor(get(urlEqualTo("/batey-service")) | |
.willReturn(aResponse() | |
.withStatus(200) | |
.withBody("Original body"))); | |
// .withTransformers("CopiesBateyHeaders"))); // no auth header copying this time | |
AwesomeHttpClient awesomeHttpClient = new AwesomeHttpClient("http://localhost:8089"); | |
//when | |
awesomeHttpClient.callBatey(); | |
//then - exception expected | |
} |
Well I hate noise in tests, and we want a single test making sure we throw an error if the header isn't copied but for all the rest of the behaviour (obviously there isn't any in this example) we can now forget about the fact our dependency should copy the headers, thus reducing noise in the priming of all our other tests.
I find this particularly important in black box acceptance tests, which often get very noisy.
I love open source :) All the code for this example is on my github here.
1 comment:
Hi,
At first thanks for the post, it is very usefull!! :)
I have one question for you.
I start my wiremock server as
java -cp "wiremock-extension-example.jar:wiremock-standalone-2.6.0.jar" com.github.tomakehurst.wiremock.standalone.WireMockServerRunner --port 9001 --verbose -- extensions info.batey.wiremock.extension.CopiesBateyHeaders --global-response-templating
And it works properly but... now?
How can I try the class extended does effect in my request?
I tried call to http://localhost:9001/anything/Bateyservice or http://localhost:9001/anything/Batey or http://localhost:9001/Batey
And i cant see any response modificated... what is the answer expected ??
Thanks and regards
Post a Comment