Wiremock Stubbing, Stub Verification And Proxying With Example

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 request to other host depending on parameters set in request stub.

Let’s start!!

Wiremock - Stubbing, Verification and Proxying

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.

Proxying - Wiremock

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 as 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.

Request Matching - Wiremock

What Response Parameters Can Be Stubbed?

For a request, we can stub all the parameters of a response as shown below:

  1. Response body
  2. Status code
  3. Response headers
  4. 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:

  1. URL  i.e. the url of the incoming request.
  2. HTTP Method – E.g. GET, PUT, POST, DELETE
  3. Query string parameters
  4. HTTP Headers including auth headers.
  5. Cookies
  6. Request body – In the case of HTTP POST / PUT.
  7. 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 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 matchers – like URL, Http Method, header match, etc.

In general, the sequence of the events is like this:

  1. Configure a Stub with Wiremock for a request.
  2. Execute the request (or send the request to Wiremock server)
  3. Ensure that the request match is successful and stubbed response is returned.
  4. 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, simply 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 i.e.the number of times the request was sent to the Wiremock server (or the number of times a request match 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 setup 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 for understanding the recorder concept.

Wiremock- 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.

Wiremock Recorder

#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.

Wiremock Recorder - Start

#5) Now, we will need to send a request through the Wiremock server that will be proxied to this host. The request should be a valid request on the target host. Let’s try using the URL – https://reqres.in/api/users/2

If you hit the above URL, then you will get the response as shown 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.

Stub Mapping

#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 the response of 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 external service and use them as mappings for any future requests to the external service.

Overall, Wiremock is a powerful tool to have a 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 speedier and more robust development cycle.

PREV Tutorial