In this Tutorial, we will Explore Three Important Concepts of Wiremock in detail i.e. Stubbing, Stub Verification & Proxying:
Stubbing in simple terms is pre-configuring responses to requests, Stub verification is verifying or validating that our stub was called and Proxying is routing requests to other hosts depending on parameters set in the request stub.
Let’s start!!
Table of Contents:
Proxying
With Wiremock, you can configure your requests to go to other hosts selectively. Proxying is similar to stubbing, which uses the method “proxiedFrom()” in stub configuration itself.
If you are curious about proxying as a concept, then please refer to the below diagram to get more clarity on the concept.
Now, let’s see proxying in action.
Suppose you have 2 services running, out of which 1 is available and another is still under development that needs to be served with stubbed responses. Let’s name them serviceA and serviceB respectively.
Now, we want to configure our Wiremock instance in such a way that all the requests coming for serviceA should be proxied to actual serviceA host and all the requests to serviceB will be served from preconfigured Wiremock responses.
Here are the steps:
#1) Start the Wiremock server standalone jar using the below command.
java -jar wiremock-standalone-2.25.1.jar
#2) Now once Wiremock server has started, execute the below curl command (you can also import this curl command into Postman and execute from there).
curl --location --request POST 'http://localhost:8080/__admin/mappings' \ --header 'Content-Type: application/json' \ --data-raw '{"request": { "urlPattern": "/serviceB/.*", "method": "GET" }, "response": { "status": 200, "proxyBaseUrl": "http://www.google.com/" } }
#3) The above command will create a mapping in Wiremock and will route all the requests URL matching “/serviceB” to host www.google.com
This means that, suppose you request for the URL – http://localhost:8080/serviceB/user/1 -> then your request will actually be proxied to the configured host in the Wiremock mappings to – http://www.google.com/sericeB/user/1
#4) You can change the hostnames (to proxy the request to) as well as the matching URL patterns as per your requirement.
Note: All the stub and proxying features/functionality could be achieved via both Wiremock standalone jar as well as programmatically through Wiremock language bindings in Java.
Stubbing & Request Matching
Stubbing is the core concept of Wiremock that enables us to return a mocked or pre-configured response for a request made to the Wiremock server.
Given below is the process or sequence that is followed:
- First stubs are configured on a Wiremock instance.
- Now, when a request is sent to the Wiremock server, then the request matching logic executes.
- If the matcher matches some stub, then the user is sent back to the matched or configured response.
- Else an error message stating that no match/stub is found.
What Response Parameters Can Be Stubbed?
For a request, we can stub all the parameters of a response as shown below:
- Response body
- Status code
- Response headers
- Response redirect etc.
Let’s see some examples of stubbing using both Java code as well as JSON requests that could be sent directly to the Wiremock standalone server.
Please note that for validating stubs set by Java code, you will need to use some REST client example i.e. OkHttp, Retrofit, etc that will make a request to the stubbed URL, while for testing stubs against standalone Wiremock, you could use CuRL from the command line or simply the browser for GET requests or simply use Postman to send requests.
#1) Sending stubbed response as text.
– Stubbing for a get request
Java Code:
private void configureStubs() { configureFor("localhost", 8080); stubFor(get(urlEqualTo("/users/1")) .willReturn(aResponse().withBody("Username is test user!"))); }
JSON: Send a POST request to – http://localhost:8080/__admin/mappings with the JSON request body
{ "request": { "method": "GET", "url": "/users/1" }, "response": { "status": 200, "body": "Username is test user!" } }
– Stubbing for a post request
Java Code:
private void configureStubsForPost() { configureFor("localhost", 8080); stubFor(post(urlEqualTo("/users")) .willReturn(aResponse().withBody("User created!"))); }
JSON:
{ "request": { "method": "POST", "url": "/users" }, "response": { "status": 200, "body": "User created!" } }
#2) Sending response headers & status along with stub.
Let’s see adding a response header for the stubbed request along with status code.
We will use the same HTTP Post request as the previous example of stubbing request to “/users” endpoint and return a status code of 201 and response header “Content-Type” with value as “text/plain/”
Java Code:
private void configureStubsForPostWithHeader() { configureFor("localhost", 8080); stubFor(post(urlEqualTo("/users")) .willReturn(aResponse() .withStatus(201) .withHeader("content-type","text/plain") .withBody("User created!"))); }
JSON:
{ "request": { "method": "POST", "url": "/users" }, "response": { "status": 201, "headers":{ "Content-Type":"text/plain" }, "body": "User created!" } }
#3) Sending response as a file (present on the file system).
We will now configure our GET request for url “/users/1” to send the response as a JSON file that’s stored on the file system.
An important point to note here is that the Wiremock Server expects the file to be served from a directory named “__files”. So for running as a JUnit Test, create a directory named “__files” at location src/test/resources
Similarly, for serving the file content with a standalone Wiremock server, you can put the files in “__files” directory that gets auto-created when the Wiremock server is started in standalone mode (Please note that the folders __files and __mappings are automatically created in the same directory where the Wiremock jar file is present).
Let’s see how the Java code and JSON request looks like Java code:
private void configureStubsForGertWithHeaderAndResponseAsJson() { configureFor("localhost", 8080); stubFor(get(urlEqualTo("/users/1")) .willReturn(ok() .withHeader("content-type","application/json") .withBodyFile("test.json"))); }
JSON:
{ "request": { "method": "GET", "url": "/users/1" }, "response": { "status": 200, "headers": { "Content-Type": "application/json" }, "bodyFileName": "test.json" } }
Request Matching
Request matching is the way through which the Wiremock server matches the incoming request and maps them to the configured stubs respectively.
Wiremock supports request matching through the following request fields:
- URL i.e. the url of the incoming request.
- HTTP Method – E.g. GET, PUT, POST, DELETE
- Query string parameters
- HTTP Headers including auth headers.
- Cookies
- Request body – In the case of HTTP POST / PUT.
- Request Form Data
Also, please note that when any of these are used in combination to specify a mapping, then it’s essentially an AND condition and all the matching parameters should match for the incoming request.
What this means is that assume we combine URL, Http Method, Query string and Header attributes in request mapping as below:
{ "request": { "urlPath": "/test/combination", "method": "POST", "headers": { "content-type": { "equalTo": "application/json" } }, "queryParameters": { "id": { "equalTo": "1" } } }, "response": { "status": 200, "body": "valid combination" } }
Then until the incoming request matches all these 4 parameters, the stub will not be matched. So to get the stubbed response with a match, you will have to create the request as below.
curl --location --request POST 'http://localhost:8080/test/combination?id=1' \ --header 'Content-Type: application/json'
The above request matches:
- URL
- Http Method – POST
- Query string parameter id=1
- And Header – “Content-type” – “application/json”
Stub Priority
Stub priority allows you to assign priorities for the request mappings. In other words, it’s simply a way to resolve which stub mapping should be given more precedence if a request matches 2 or more stub mappings.
For example, for a single service – you have a total of 5 endpoints of which 2 are having specific responses and 3 are having common responses in stub mappings.
Now one way could be to configure each of the 5 endpoints via separate mappings, but this would get cumbersome and bloat the mappings once you start configuring multiple endpoints. For scenarios like these, we can use stub priorities.
We will define stub mappings for requests having specific responses and assign the highest priority to them. For generic responses, we can create a single mapping and assign a lower priority.
Let’s see this example in action with Java code as well as JSON mapping request to a standalone Wiremock server.
Java Code:
Here you can see that we have configured 3 stubs – one each for endpoints /service/endpoint1 and /service/endpoint2 and a third configuration with a lower priority for the url’s other than the ones that have specific mappings.
private void configureStubsForStubPriorityDemo() { configureFor("localhost", 8080); stubFor(get(urlEqualTo("/service/endpoint1")) .atPriority(1) .willReturn(ok() .withBody("endpoint1"))); stubFor(get(urlEqualTo("/service/endpoint2")) .atPriority(1) .willReturn(ok() .withBody("endpoint2"))); stubFor(get(urlMatching("/service/.*")) .atPriority(5) .willReturn(ok() .withBody("endpoint not implemented"))); }
JSON Mappings:
Mapping 1 – Specific mapping for /endpoint1 with a higher priority.
{ "priority": 1, "request": { "method": "GET", "url": "/service/endpoint1" }, "response": { "status": 200, "body": "endpoint1" } }
Mapping 2 – Specific mapping for /endpoint2 with a higher priority.
{ "priority": 1, "request": { "method": "GET", "url": "/service/endpoint2" }, "response": { "status": 200, "body": "endpoint2" } }
Mapping 3 – Generic mapping for any other endpoints (i.e. other than endpoint1 and endpoint2) at a lower priority.
{ "priority": 5, "request": { "method": "GET", "urlPattern": "/service/.*" }, "response": { "status": 200, "body": "endpoint not implemented" } }
Stub priority can also be used to define a default response in Wiremock for all the endpoints that are not having any stubs.
If not configured, Wiremock will return its generic response like “No matching stubs found” etc., but stub priority allows you to configure that by creating a catch-all kind of stub with a lower priority value.
Let’s look at the JSON mapping for such a stub.
{ "priority":100, "request": { "method": "ANY", "urlPattern": ".*" }, "response": { "status": 404, "jsonBody": {"status":"Not found","message":"Endpoint not found or configured"}, "headers": { "Content-Type": "application/json" } } }
Video Tutorials: Stubbing and Proxying
Stub Verification
Verification comes after stubbing and request matching.
Verification is nothing but checking with the Wiremock server as to whether a given request was received by it or not. It’s analogous to what we do with Mocks in unit testing. As part of the test assertion, for a mock, we check or validate whether the mock interacted or not to mark the test as failed or successful.
Wiremock can record all the requests that were sent to it (before it’s restarted or being explicitly reset). Verification can be done both at the code level through Java bindings or through the JSON requests for standalone Wiremock.
Verification is similar to request match and you can define verification parameters similar to request matches – like URL, HTTP Method, header match, etc.
In general, the sequence of the events is like this:
- Configure a Stub with Wiremock for a request.
- Execute the request (or send the request to the Wiremock server)
- Ensure that the request match is successful and the stubbed response is returned.
- Verify the request through verification API.
Let’s see a couple of examples to understand this.
We are setting a stub for url – /users/1 with Http Method GET as below
{ "request": { "method": "GET", "url": "/users/1" }, "response": { "status": 200, "headers": { "Content-Type": "text/plain" }, "body": "Username is test user!" } }
Java Code:
private void configureStubs() { configureFor("localhost", 8080); stubFor(get(urlEqualTo("/users/1")) .willReturn(ok() .withHeader("content-type","text/plain") .withBody("Username is test user!"))); }
Now let’s write JSON request and Java code to verify the request.
Send a POST request to – http://localhost:8080/__admin/requests/count with JSON body as shown below.
{ "method": "GET", "url": "/users/1" }
For Java code, add a verify/assert as shown below.
verify(exactly(1),getRequestedFor(urlEqualTo("/users/1")));
Now, if you look closely at both of the verification code samples, in verification we are doing nothing but getting the count the number of times the request was sent to the Wiremock server (or the number of times a request matches to stub was successful)
So, in the above case, if we execute these requests without hitting the stub then we will get the count as 0 (and the assert will fail as it’s looking for exactly 1 match). For JSON validation, you can simply hit the Wiremock server with a CuRL request as shown below.
curl --location --request GET 'http://localhost:8080/users/1' \ --header 'Content-Type: application/json'
Try hitting the verification request to the server and notice that you will now get the count as 1. Try repeating the same steps and you will now see the count increasing to 2, 3, etc.
It’s possible to create a complex verification match depending on how stubs are set up but in general, the concept is just to validate whether a successful matching request was received by the Wiremock server or not.
Video Tutorial: Stub Verification
Wiremock: Record Stub Mappings
Wiremock provides a recorder interface to record or capture stub mappings by proxying through an actual URL. This is helpful to create mappings automatically without having to manually configure or create mappings in the Wiremock server.
Refer to the below diagram to understand the recorder concept.
Let’s see Recorder in action through Wiremock standalone.
#1) Start the Wiremock server in a standalone mode using the jar library.
java -jar wiremock-standalone-2.25.1.jar
#2) Now, navigate to URL – http://localhost:8080/__admin/recorder/ in the browser. It should open the Wiremock recorder console.
#3) Now, enter the target URL as Reqres which is a fake data API server available over the internet. We will use this to illustrate the concept of the Wiremock recorder.
Target URL here simply means the URL of the host where you want requests to be proxied to and the response returned would be captured as a stub.
#4) After entering the URL, click on the “Record” button. You will see an indicator that the Wiremock recorder is now listening and recording the requests.
#5) Now, we will need to send a request through the Wiremock server that will be proxied to this host. The request should be valid for the target host. Let’s try using the URL – https://reqres.in/api/users/2
If you hit the above URL, you will get the response below.
{ "data": { "id": 2, "email": "janet.weaver@reqres.in", "first_name": "Janet", "last_name": "Weaver", "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg" } }
What this means is that this is a valid request for the target host.
#6) Now, to capture this request as a stub, we will need to execute this request through Wiremock. Use Postman or simply a browser to execute the URL – http://localhost:8080/api/users/2
#7) After Step 6, navigate to the Wiremock recorder console, and click on the “Stop” button.
If any stubs were captured, then you will see the count for the mappings captured as in the screenshot below.
#8) Now this indicates that the stub was successfully captured. To validate, we can try fetching mappings through Wiremock admin API – by executing – http://localhost:8080/__admin/mappings/
You can see the details of the captured stub as a response to the above URL.
{ "mappings" : [ { "id" : "1a4bbdab-405b-49c4-95fb-42a985ce0243", "name" : "api_users_2", "request" : { "url" : "/api/users/2", "method" : "GET" }, "response" : { "status" : 304, "headers" : { "Date" : "Mon, 30 Dec 2019 05:22:57 GMT", "Connection" : "keep-alive", "Set-Cookie" : "__cfduid=d713b16028ebc388a910a569f56fc08d31577683377; expires=Wed, 29-Jan-20 05:22:57 GMT; path=/; domain=.reqres.in; HttpOnly; SameSite=Lax; Secure", "X-Powered-By" : "Express", "Access-Control-Allow-Origin" : "*", "Etag" : "W/\"aa-yZW/45DWGt/1ri05OLnMt/FJ3RY\"", "Via" : "1.1 vegur", "Cache-Control" : "max-age=14400", "CF-Cache-Status" : "HIT", "Age" : "1092", "Expect-CT" : "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", "Vary" : "Accept-Encoding", "Server" : "cloudflare", "CF-RAY" : "54d1943438cfdd12-SIN" } }, "uuid" : "1a4bbdab-405b-49c4-95fb-42a985ce0243", "persistent" : true } ], "meta" : { "total" : 1 } }
#9) Now any request that will be made to this URL i.e http://localhost:8080/api/users/2 will be served from the stub.
Video Tutorial: Wiremock Recorder
Conclusion
In this tutorial, we walked through the core concepts of Wiremock i.e. stubbing, proxying, and verifying. We also discussed, how we can use Wiremock Recorder to capture stubs by proxying through an external service and use them as mappings for any future requests to the external service.
Overall, Wiremock is a powerful tool to have solid coverage through integration testing as well as a very helpful tool for developers to develop an application against services that are not always available or are still under development but using Wiremock, it could be configured to return the desired response thereby helping in the speedier and more robust development cycle.