WireMock Tutorial: Request Matching, Part Two

This blog post describes how we can use the request matching support of WireMock when we want specify expectations for cookies, HTTP headers, and request parameters. After we have finished this blog post, we:

  • Can compare the actual cookie value with the expected cookie value.
  • Know how we can compare the actual header value with the expected header value.
  • Understand how we can compare the actual request parameter value with the expected request parameter value.

Let's begin.

Getting Started

As we remember, before we can specify our expectations for the other attributes of an HTTP request, we have to specify the expected request method and request URL. After we have done this, we can specify our other expectations by using a MappingBuilder object.

For example, if we expect that our WireMock server receives a GET request to the URL path: '/api/message', we can create the required MappingBuilder object by using the following code:

import com.github.tomakehurst.wiremock.WireMockServer;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static org.assertj.core.api.Assertions.assertThat;

class RequestMatchingTest {

    private RestTemplate restTemplate;
    private WireMockServer wireMockServer;

    @Test
    @DisplayName("Should compare the actual URL path with the expected URL path")
    void shouldCompareActualUrlWithExpectedUrlRegex() {
        givenThat(get(urlPathEqualTo("/api/message"))
                //Specify the other expectations here
                .willReturn(aResponse().withStatus(200))
        );

        String serverUrl = buildApiMethodUrl(1L);
        ResponseEntity<String> response = restTemplate.getForEntity(serverUrl,
                String.class
        );
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }


    private String buildApiMethodUrl(Long messageId) {
        return String.format("http://localhost:%d/api/message?id=%d",
                this.wireMockServer.port(),
                messageId
        );
    }
}
I removed the @BeforeEach and @AfterEach methods from the code examples of this blog post because they add no value to this blog post. If you don't know how you can configure WireMock, you should read this blog post.

When we want to specify our expectations for cookies, HTTP headers, and request parameters, we have to use these methods provided by the MappingBuilder interface:

  • The withCookie() method allows us to compare the actual cookie value with the expected cookie value.
  • The withHeader() method allows us to compare the actual header value with the expected header value.
  • The withQueryParam() method allows to compare the actual request parameter value with the expected request parameter value.

These methods take two method parameters:

  1. The name of the cookie, header, or request parameter.
  2. A StringValuePattern object which specifies the expected value.
These methods return a reference to used MappingBuilder object. This means that we can specify our expectations by using a fluent API.

We can create the required StringValuePattern object by using the static factory methods of the WireMock class. When we specify the expected value of a cookie, a header, or a request parameter, we can use these five methods:

  • The equalTo() method ensures that the actual value is equal to the string given as a method parameter.
  • The equalToIgnoreCase() method ignores case and ensures that the actual value is equal to the string given as a method parameter.
  • The containing() method ensures that the actual value contains the string given as a method parameter.
  • The matching() method ensures that the actual value matches with the regex given as a method parameter.
  • The notMatching() method ensures that the actual value doesn't match with the regex given as a method parameter.
I won't describe how you can use all these methods because this blog post would become too long. That being said, the example application of this blog post has multiple examples which demonstrate how these methods work.

Next, we will find out how we can specify expectations for cookies.

Specifying Expectations for Cookies

If we expect that the value of the name cookie is equal to: 'Petri Kainulainen', we must specify our expectation by using the withCookie() method of the MappingBuilder interface.

After we have specified our expectation, the source code of our test class looks as follows:

import com.github.tomakehurst.wiremock.WireMockServer;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static org.assertj.core.api.Assertions.assertThat;

class RequestMatchingTest {

    private RestTemplate restTemplate;
    private WireMockServer wireMockServer;

    @Test
    @DisplayName("Should compare the actual value with the expected value")
    void shouldCompareActualUrlWithExactExpectedUrl() {
        givenThat(get(urlEqualTo("/api/message?id=1"))
                .withCookie("name", equalTo("Petri Kainulainen"))
                .willReturn(aResponse().withStatus(200))
        );

        String apiMethodUrl = buildApiMethodUrl(1L);
        HttpEntity<String> httpRequest = createHttpRequest();

        ResponseEntity<String> response = restTemplate.exchange(apiMethodUrl,
                HttpMethod.GET,
                httpRequest,
                String.class
        );
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }

    private HttpEntity<String> createHttpRequest() {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cookie", "name=Petri Kainulainen");
        return new HttpEntity<>(headers);
    }

    private String buildApiMethodUrl(Long messageId) {
        return String.format("http://localhost:%d/api/message?id=%d",
                this.wireMockServer.port(),
                messageId
        );
    }
}
The CookieMatchingTest class demonstrates how we can specify different expectations for cookie values.

Let's move on and find out how we can specify expectations for HTTP Headers.

Specifying Expectations for HTTP Headers

If we expect that the value of the Accept header is equal to: 'application/json;charset=UTF-8', we must specify our expectation by using the withHeader() method of the MappingBuilder interface.

After we have specified our expectation, the source code of our test class looks as follows:

import com.github.tomakehurst.wiremock.WireMockServer;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static org.assertj.core.api.Assertions.assertThat;

class RequestMatchingTest {

    private RestTemplate restTemplate;
    private WireMockServer wireMockServer;

    @Test
    @DisplayName("Should compare the actual value with the exact expected value")
    void shouldCompareActualUrlWithExactExpectedUrl() {
        givenThat(get(urlEqualTo("/api/message?id=1"))
                .withHeader("Accept", equalTo(MediaType.APPLICATION_JSON_UTF8_VALUE))
                .willReturn(aResponse().withStatus(200))
        );

        String apiMethodUrl = buildApiMethodUrl(1L);
        HttpEntity<String> httpRequest = createHttpRequest();

        ResponseEntity<String> response = restTemplate.exchange(apiMethodUrl,
                HttpMethod.GET,
                httpRequest,
                String.class
        );
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }

    private HttpEntity<String> createHttpRequest() {
        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(
                Collections.singletonList(MediaType.APPLICATION_JSON_UTF8)
        );
        return new HttpEntity<>(headers);
    }

    private String buildApiMethodUrl(Long messageId) {
        return String.format("http://localhost:%d/api/message?id=%d",
                this.wireMockServer.port(),
                messageId
        );
    }
}
The HeaderMatchingTest class demonstrates how we can specify different expectations for header values.

If we are using basic authentication and we want to ensure that our WireMock server receives an HTTP request that has the correct username and password, we have specify the expected value of the Authorization header.

However, because it is quite cumbersome to construct a correctly encoded header value, the MappingBuilder interface declares a method that does the heavy lifting for us. This method is called withBasicAuth() and it takes the expected username and password as method parameters.

After we have ensured that the Authentication header contains the username: 'username' and password: 'password', the source code of our test class looks as follows:

import com.github.tomakehurst.wiremock.WireMockServer;
import org.apache.commons.codec.binary.Base64;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.Charset;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static org.assertj.core.api.Assertions.assertThat;

class RequestMatchingTest {

    private RestTemplate restTemplate;
    private WireMockServer wireMockServer;

    @Test
    @DisplayName("Should ensure that request has correct username and password")
    void shouldEnsureThatRequestHasCorrectUsernameAndPassword() {
        givenThat(get(urlEqualTo("/api/message?id=1"))
                .withBasicAuth("username", "password")
                .willReturn(aResponse().withStatus(200))
        );

        String apiMethodUrl = buildApiMethodUrl(1L);
        HttpEntity<String> httpRequest = createHttpRequest();

        ResponseEntity<String> response = restTemplate.exchange(apiMethodUrl,
                HttpMethod.GET,
                httpRequest,
                String.class
        );
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }

    private HttpEntity<String> createHttpRequest() {
        HttpHeaders headers = new HttpHeaders();

        String auth = "username:password";
        byte[] encodedAuth = Base64.encodeBase64(
                auth.getBytes(Charset.forName("US-ASCII"))
        );
        String authHeader = "Basic " + new String(encodedAuth);
        headers.set("Authorization", authHeader);

        return new HttpEntity<>(headers);
    }

    private String buildApiMethodUrl(Long messageId) {
        return String.format("http://localhost:%d/api/message?id=%d",
                this.wireMockServer.port(),
                messageId
        );
    }
}
The BasicAuthMatchingTest class demonstrates how we can specify the expected value of the Authentication header.

Next, we will find out how we can specify expectations for request parameters.

Specifying Expectations for Request Parameters

If we expect that the value of the searchTerm request parameter is equal to: 'foobar', we must specify our expectation by using the withQueryParam() method of the MappingBuilder interface.

After we have specified our expectation, the source code of our test class looks as follows:

import com.github.tomakehurst.wiremock.WireMockServer;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static org.assertj.core.api.Assertions.assertThat;

class RequestMatchingTest {

    private RestTemplate restTemplate;
    private WireMockServer wireMockServer;

    @Test
    @DisplayName("Should compare the actual value with the expected value")
    void shouldCompareActualUrlWithExactExpectedUrl() {
        givenThat(get(urlPathEqualTo("/api/message"))
                .withQueryParam("searchTerm", equalTo("foobar"))
                .willReturn(aResponse().withStatus(200))
        );

        String apiMethodUrl = buildApiMethodUrl("foobar");

        ResponseEntity<String> response = restTemplate.getForEntity(apiMethodUrl,
                String.class
        );
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }

    private String buildApiMethodUrl(String searchTerm) {
        return String.format("http://localhost:%d/api/message?searchTerm=%s",
                this.wireMockServer.port(),
                searchTerm
        );
    }
}
There are three things I want to point out:

  • If the expected HTTP request has multiple request parameters, we should use this approach because it makes our tests easier to read and write. Also, this technique allows us specify our expectations in a way that doesn’t depend on the order of the expected request parameters
  • If we are specifying expectations for request parameters by using the withQueryParam() method, we should configure the expected URL by using path based matching because it doesn't make any sense to check the same expectation multiple times.
  • The RequestParameterMatchingTest class demonstrates how we can specify different expectations for request parameter values.

We can now specify our expectations for cookies, headers, and request parameters. Let's summarize what we learned from this blog post.

Summary

This lesson has taught us five things:

  • Before we can specify our expectations for the other attributes of an HTTP request, we have to specify the expected request method and request URL.
  • When we want to specify our expectations for cookies, HTTP headers, and request parameters, can use the methods declared by the MappingBuilder interface.
  • We can specify our expectations by using a fluent API.
  • We can create a new StringValuePattern object, which specifies the expected value, by using the static factory methods of the WireMock class.
  • If we are specifying expectations for request parameters by using the withQueryParam() method, we should configure the expected URL by using path based matching.

P.S. You can get the example application of this blog post from Github.

1 comment… add one

Leave a Reply