Using WireMock in your unit tests

1. Introduction

Most non-trivial applications, especially in today’s microservices world, will call other services using a REST API to perform a task.

Unit testing parts of your application that call REST APIs can be difficult and error prone when just using JUnit and Mockito.

If you use Spring’s RestTemplate to call REST services, you could inject a mocked instance of it into your subject under test and stub the relevant method:

Java

RestTemplate restTemplate = mock(RestTemplate.class);
TodoList list = new TodoList("1", "To do today");
Mockito
    .when(restTemplate.getForObject(
        "http://localhost:8080/lists/1", 
        TodoList.class)
    ).thenReturn(list);

Kotlin

val restTemplate = mock(RestTemplate::class.java)
val list = TodoList("1", "To do today")
Mockito.
    `when`(restTemplate.getForObject(
        "http://localhost:8080/lists/1", 
        TodoList::class.java)
    ).thenReturn(list)

But to know exactly which RestTemplate method to stub (getForObject?, getForEntity?, exchange?) , you need to dig into the implementation details of your class, which means your test knows more than it really should. We always prefer black box tests as they are more maintainable: if the implementation of the subject under test changes in a way that doesn’t change the behavior, a black box test doesn’t require any changes either.

Since the interface between our application and the external REST API is HTTP, it would actually be a lot easier if we could stub an actual HTTP request/response pair.

2. Enter Wiremock

WireMock is a tool for mocking HTTP responses and although it can run standalone, it also integrates very nicely into unit tests. Wiremock will simply:

  1. spin up a Jetty server
  2. respond to HTTP requests that are triggered from your unit test according to stubbed behaviour you define in your unit test
  3. stop the Jetty server when your unit test has finished

To use it, add the dependency to your pom.xml or build.gradle:

Maven

<dependency>
  <groupId>com.github.tomakehurst</groupId>
  <artifactId>wiremock-jre8</artifactId>
  <version>2.26.3</version>
  <scope>test</scope>
  <exclusions>
    <exclusion>
      <groupId>org.ow2.asm</groupId>
      <artifactId>asm</artifactId>
    </exclusion>
  </exclusions>
</dependency>

Gradle

testImplementation 'com.github.tomakehurst:wiremock-jre8:2.26.3' {
  exclude group: 'asm', module: 'asm'
}

Two things to note here:

  1. Use the wiremock-jre8 artifact unless you need to be on JDK 7 in which you should use the wiremock one instead: it holds back the Jetty dependency at version 9.2.28.v20190418 which is the last version to support JDK 7
  2. The asm exclusion is to avoid a dependency conflict if another dependency in your project (transitively) depends on asm. Wiremock uses json-path which uses asm for some features, but Wiremock doesn’t seem to use any of those features, so it’s safe to exclude the asm dependency.

For JUnit 4, a WireMockRule is available that will automatically start the WireMock server before every test and stop it after, but this has not been ported to a JUnit 5 extension. And it’s really just a few lines of code:

Java

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;

class MyServiceTest {

  private WireMockServer wireMockServer;

  @BeforeEach
  public void setup() {
    wireMockServer = new WireMockServer(WireMockConfiguration
        .wireMockConfig()
        .dynamicPort());
    wireMockServer.start();
  }

  @AfterEach
  public void tearDown() {
    wireMockServer.stop();
  }
}

Kotlin

import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import com.github.tomakehurst.wiremock.WireMockServer
import com.github.tomakehurst.wiremock.core.WireMockConfiguration

internal class MyServiceTest {

  private lateinit var wireMockServer: WireMockServer

  @BeforeEach
  fun setup() {
    wireMockServer = WireMockServer(WireMockConfiguration
        .wireMockConfig()
        .dynamicPort())
    wireMockServer.start()
  }

  @AfterEach
  fun tearDown() {
    wireMockServer.stop()
  }

}

WireMockConfiguration.wireMockConfig().dynamicPort() creates a WireMockConfiguration that indicates the server can listen on any available port. This is convenient as a predefined port hard-coded into your unit test might not always be available, especially on a continuous build server like Jenkins where several tests might be running at the same time. Once the server has started you can get the actual port using wireMockServer.port(), or the entire base URL of the server using wireMockServer.baseUrl().

We recommend using wireMockServer.baseUrl() as it will include the host name or IP of the actual interface WireMock is listening on: we’ve seen cases where instead of using localhost WireMock picked a physical network interface to listen on and tests that had hard-coded "localhost" suddenly failed.

That’s it! We have a web server running in our unit test. Time to add some behavior to it so it can act as a stand-in for the real-life REST API our application calls.

3. Stubbing HTTP responses

Wiremock will let you create stubs for almost anything HTTP can do. The main method here is WireMockServer.stubFor, which will let you stub based on the HTTP method (GET, POST, PUT, etc.), request headers, request body, etc.; Wiremock calls the parameters to which a stub applies the mapping.

Let’s start with a stub for a simple GET mapping.

3.1. Stubbing a GET response

Let’s say you have a UserService that has a getUsers() method that does a GET call to a REST service at <baseUrl>/users, e.g. https://example.com/api/users, which returns the users as a JSON response:

Java

@lombok.Data
public class User {

  private final String username;

  @JsonCreator
  public User(@JsonProperty("username") String username) {
    this.username = username;
  }
}

@Service
public class UserService {

  private String baseUrl;

  private RestTemplate restTemplate;

  public UserService(
      @Value("${userservice.url}") String baseUrl, 
      RestTemplate restTemplate
  ) {
    this.baseUrl = baseUrl;
    this.restTemplate = restTemplate;
  }

  public List<User> getUsers() {
    ResponseEntity<List<User>> responseEntity = restTemplate.exchange(
        baseUrl + "/users", 
        HttpMethod.GET, 
        null, 
        new ParameterizedTypeReference<>() { }
    );
    return responseEntity.getBody();
  }
}

Kotlin

class User @JsonCreator constructor(
    @JsonProperty("username")
    val username: String
)

@Service
class UserService(
    @Value("\${userservice.url}") private val baseUrl: String, 
    private val restTemplate: RestTemplate
) {
  fun getUsers(): List<User> {
    val responseEntity = restTemplate.exchange(
      "$baseUrl/users", 
      HttpMethod.GET, 
      null, 
      object : ParameterizedTypeReference<List<User>>() { })
    return responseEntity.body!!
  }
}

To test it with a REST service mocked by WireMock, you’ll stub a GET response, using WireMock.get, and then point your UserService to WireMock instead of to the real REST service:

Java

import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;

public class UserServiceTest {

  private UserService userService;

  private WireMockServer wireMockServer;

  @BeforeEach
  public void setup() {
    wireMockServer = new WireMockServer(WireMockConfiguration
        .wireMockConfig()
        .dynamicPort());
    wireMockServer.start();

    userService = new UserService(
        wireMockServer.baseUrl(), 
        new RestTemplateBuilder().build());
  }

  @AfterEach
  public void tearDown() {
    wireMockServer.stop();
  }

  @Test
  public void testGetUsers() {
    // Given
    wireMockServer.stubFor(
        get(urlEqualTo("/users"))
            .willReturn(
                aResponse()
                    .withStatus(200)
                    .withHeader("Content-Type", "application/json")
                    .withBody("[\n" +
                        "  { \"username\": \"john\" },\n" +
                        "  { \"username\": \"mary\" }\n" +
                        "]")
            )
    );

    // When
    List<User> users = userService.getUsers();

    // Then
    assertThat(users, contains(
        hasProperty("username", is("john")),
        hasProperty("username", is("mary"))
    ));
  }
}

Kotlin

import com.github.tomakehurst.wiremock.client.WireMock.get
import com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo
import com.github.tomakehurst.wiremock.client.WireMock.aResponse

class UserServiceTest {
  private lateinit var userService: UserService

  private lateinit var wireMockServer: WireMockServer

  @BeforeEach
  fun setup() {
    wireMockServer = WireMockServer(WireMockConfiguration
        .wireMockConfig()
        .dynamicPort())
    wireMockServer.start()

    userService = UserService(
        wireMockServer.baseUrl(), 
        RestTemplateBuilder().build())
  }

  @AfterEach
  fun tearDown() {
    wireMockServer.stop()
  }

  @Test
  fun testGetUsers() {
    // Given
    wireMockServer.stubFor(
        get(urlEqualTo("/users"))
            .willReturn(
                aResponse()
                    .withStatus(200)
                    .withHeader("Content-Type", "application/json")
                    .withBody("""
                        [
                          { "username": "john" },
                          { "username": "mary" }
                        ]""".trimIndent())
            )
    )

    // When
    val users = userService.getUsers()

    // Then
    assertThat(users, contains(
        hasProperty("username", `is`("john")),
        hasProperty("username", `is`("mary"))
    ))
  }
}

3.1.1. Matching on query parameters

If your request has query parameters, you can also match on query parameters by including them in the relative URL you pass to urlEqualTo.

Let’s add a method getUsersByUsername to our UserService which uses a query parameter when calling the REST service:

Java

public List<User> getUsersByName(String username) {
  ResponseEntity<List<User>> responseEntity = restTemplate.exchange(
      baseUrl + "/users?name={name}", 
      HttpMethod.GET, 
      null, 
      new ParameterizedTypeReference<>() { }, 
      username);
  return responseEntity.getBody();
}

Kotlin

fun getUsersByName(username: String): List<User> {
  val responseEntity = restTemplate.exchange(
      "$baseUrl/users?name={name}",
      HttpMethod.GET,
      null,
      object : ParameterizedTypeReference<List<User>>() { },
      username)
  return responseEntity.body!!
}

We can test this as follows:

Java

@Test
public void testGetUsersByName() {
  // Given
  wireMockServer.stubFor(
      get(urlEqualTo("/users?name=john"))
          .willReturn(
              aResponse()
                  .withStatus(200)
                  .withHeader("Content-Type", "application/json")
                  .withBody("[\n" +
                      "  { \"username\": \"john\" }\n" +
                      "]")
          )
  );

  // When
  List<User> users = userService.getUsersByName("john");

  // Then
  assertThat(users, contains(
      hasProperty("username", is("john"))
  ));
}

Kotlin

@Test
fun testGetUsersByName() {
  // Given
  wireMockServer.stubFor(
      get(urlEqualTo("/users?name=john"))
          .willReturn(
              aResponse()
                  .withStatus(200)
                  .withHeader("Content-Type", "application/json")
                  .withBody("""
                      [
                        { "username": "john" }
                      ]""".trimIndent())
          )
  )

  // When
  val users = userService.getUsersByName("john")

  // Then
  assertThat(users, contains(
      hasProperty("username", `is`("john"))
  ))
}

However, you need to URL encode the query parameters yourself if you do it like this. E.g. if you’re testing your method with a username containing an ampersand (&), you’d need to stub the REST call like this:

Java

@Test
public void testGetUsersByName() {
  // Given
  wireMockServer.stubFor(
      get(urlEqualTo("/users?name=john%26mary"))
          .willReturn(
              aResponse()
                  .withStatus(200)
                  .withHeader("Content-Type", "application/json")
                  .withBody("[\n" +
                      "  { \"username\": \"john&mary\" }\n" +
                      "]")
          )
  );

  // When
  List<User> users = userService.getUsersByName("john&mary");

  // Then
  assertThat(users, contains(
      hasProperty("username", is("john&mary"))
  ));
}

Kotlin

@Test
fun testGetUsersByName() {
  // Given
  wireMockServer.stubFor(
      get(urlEqualTo("/users?name=john%26mary"))
          .willReturn(
              aResponse()
                  .withStatus(200)
                  .withHeader("Content-Type", "application/json")
                  .withBody("""
                      [
                        { "username": "john&mary" }
                      ]""".trimIndent())
          )
  )

  // When
  val users = userService.getUsersByName("john&mary")

  // Then
  assertThat(users, contains(
      hasProperty("username", `is`("john&mary"))
  ))
}

It’s easier to let WireMock take care of that for you, by using the urlPathEqualTo method instead of urlEqualTo and adding a withQueryParam to it, like this:

Java

@Test
public void testGetUsersByName() {
  // Given
  wireMockServer.stubFor(
      get(urlPathEqualTo("/users"))
          .withQueryParam("name", equalTo("john&mary"))
          .willReturn(
              aResponse()
                  .withStatus(200)
                  .withHeader("Content-Type", "application/json")
                  .withBody("[\n" +
                      "  { \"username\": \"john&mary\" }\n" +
                      "]")
          )
  );

  // When
  List<User> users = userService.getUsersByName("john&mary");

  // Then
  assertThat(users, contains(
      hasProperty("username", is("john&mary"))
  ));
}

Kotlin

@Test
fun testGetUsersByName() {
  // Given
  wireMockServer.stubFor(
      get(urlPathEqualTo("/users"))
          .withQueryParam("name", equalTo("john&mary"))
          .willReturn(
              aResponse()
                  .withStatus(200)
                  .withHeader("Content-Type", "application/json")
                  .withBody("""
                      [
                        { "username": "john&mary" }
                      ]""".trimIndent())
          )
  )

  // When
  val users = userService.getUsersByName("john&mary")

  // Then
  assertThat(users, contains(
      hasProperty("username", `is`("john&mary"))
  ))
}

3.2. Stubbing a POST response

Our UserService should be able to create users, too. So let’s add a method for that:

Java

public String createUser(String username) {
  ResponseEntity<Void> responseEntity = restTemplate.exchange(
      baseUrl + "/users",
      HttpMethod.POST,
      new HttpEntity<>(new User(username)),
      Void.class);
  URI location = responseEntity.getHeaders().getLocation();
  int p = location.getPath().lastIndexOf('/');
  return location.getPath().substring(p + 1);
}

Kotlin

fun createUser(username: String): String {
  val responseEntity = restTemplate.exchange(
      "$baseUrl/users",
      HttpMethod.POST,
      HttpEntity(User(username)),
      Void::class.java)
  val location = responseEntity.headers.location
  val p = location!!.path.lastIndexOf('/')
  return location.path.substring(p + 1)
}

Now let’s test it using WireMock!

When stubbing a response that contains a body, you’ll want to include a matcher on the body contents in your stubbing statement. This way you can verify that your code sends the request body you’re expecting. It also means you can create two stubs for the same relative path, which might be necessary depending on what the code you’re testing does.

To create a stub for a POST call, we use the WireMock.post() method. A few things are different here from the GET stub we did in the previous section:

  • We’re usingpost(urlEqualTo("/users")) instead of geturlEqualTo("/users")), as mentioned, to match on a POST request
  • We’re including a withHeader to verify that the correct Content-Type header is being sent, since a request body should always be accompanied by a Content-Type header describing what type of data is in the body.
  • We’re not using equalTo to match on the request body, since it would do an exact String comparison. Instead, we’ll use equalToJson which does a more lenient comparison on JSON equivalence. This means that extra spaces or newlines in places where they are not significant will not break the test, nor will a different order of the fields. This way our test is more robust, without compromizing on correctness.
  • We’re stubbing our POST method to return a 201 Created response, which is the normal response for a REST call that creates a resource. We’re also including a Location header to the created resource; again, this is a REST best practice. The response has no body, so we’re leaving out the withBody part here.

Some REST services will return the resource that was created by a POST call as the response body of that POST call. If that is the case, you should include a withBody in the stub. You can then specify the (JSON) representation that the real REST service would be sending back — and you would include a withHeader as will to indicate the content type of the response body, e.g. application/json.

Java

@Test
public void testCreateUser() {
  // Given
  wireMockServer.stubFor(
      post(urlEqualTo("/users"))
          .withHeader("Content-Type", equalTo("application/json"))
          .withRequestBody(
              equalToJson("{ \"username\": \"lucy\" }"))
          .willReturn(
              aResponse()
                  .withStatus(201)
                  .withHeader("Location", 
                      wireMockServer.baseUrl() + "/users/lucy")
          )
  );

  // When
  String userId = userService.createUser("lucy");

  // Then
  assertEquals("lucy", userId);
}

Kotlin

@Test
fun testCreateUser() {
  // Given
  wireMockServer.stubFor(
      post(urlEqualTo("/users"))
          .withHeader("Content-Type", equalTo("application/json"))
          .withRequestBody(
              equalToJson("{ \"username\": \"lucy\" }"))
          .willReturn(
              aResponse()
                  .withStatus(201)
                  .withHeader("Location", 
                      wireMockServer.baseUrl() + "/users/lucy")
          )
  )

  // When
  val userId = userService.createUser("lucy")

  // Then
  assertEquals("lucy", userId)
}

4. Conclusion

WireMock is a great way to test code that does calls to external REST services. We’ve showed some examples of using WireMock to mock a REST service with both GET and POST methods.

Using this examples, you can start writing your own WireMock-based unit tests. We haven’t gone into writing negative test cases (for example, returning an error response from a WireMock stub), or using other HTTP methods like PATCH, DELETE, or OPTIONS. But it shouldn’t be too hard to adapt these examples to do just that!

The code for this blog post can be found on my GitHub: https://github.com/fransflippo/wiremock-examples

Happy WireMocking!


Have you used WireMock in your tests?
Let us know your experiences by leaving a comment!

Leave a Reply

Your email address will not be published.