Spring Boot Integration Tests With WireMock and JUnit 5

Last Updated:  August 27, 2021 | Published: November 28, 2019

In one of the previous blog posts, I demonstrated how to write integration tests with Spring Boot and Testcontainers. As the aforementioned post targeted the database setup for integration tests, you might wonder how to stub other dependencies like external systems. For such use cases, we can use WireMock to write integration tests for Spring Boot applications. As an alternative, Testcontainers also offers support for MockServer, which I'll cover in a different post. This post will focus on using WireMock for our integration tests to stub HTTP calls to external systems.

This example uses Spring Boot 2.4.5, JUnit 5, WireMock 2.27.2, and Java 11.

Spring Boot and WireMock Project Setup

First, let's cover the application setup and its dependencies.

The demo application runs on Tomcat and uses the Spring WebClient (part of Spring WebFlux) to make HTTPS calls to an external system. Hence we need the following dependencies:

Furthermore, the wiremock-jre8 dependency is required to start the WireMock server later on.

In addition to this, we use the Maven Failsafe Plugin to separate the execution of our unit and integration tests. With this setup, our unit tests run first using the Maven Surefire Plugin. Right after packaging the application, our integration tests are executed. To make this work, we have to follow a naming convention and our integration tests need the postfix IT (or any other if we configure it).

Spring Boot Application Walkthrough

Next, let's have a look at the sample application. The application offers one public endpoint to retrieve a list of todos:

For the sake of simplicity, it just returns the result of the WebClient call. To set up this WebClient, we're using the following configuration class:

By default, the application fetches random todos from a public placeholder API:

Once the application is up and running, the result of http://localhost:8080/api/todos is the following:

Integration Test Setup for WireMock and JUnit 5

As we are about to write the integration tests for this application, we may not want to call the actual HTTP endpoint during the tests. This is less critical for this example, but imagine testing logic that manipulates data within the remote application using HTTP POST or PUT. We don't want this to happen for our tests and rather talk to a stub.

Moreover, if we would connect to the actual remote system for our integration test, the test outcome depends on the availability of the remote service and produces additional load. At the same time, we may also want to test different responses (e.g., body or status code).

We can resolve the described downsides of connecting to the real remote system by using WireMock. With WireMock, we can instrument the actual response for a given URL and set the body and status code to whatever we need. Internally, WireMock spawns a local Jetty server that handles the remote calls during our integration tests.

WireMock (currently) ships with a JUnit 4 @Rule to handle starting and stopping the Jetty server. As we can't use this rule within JUnit 5, we can either write our own @Extension or use a Spring context initializer:

In this example, the initializer has the following responsibilities:

  • starting/stopping the WireMockServer on a random port
  • registering the WireMockServer bean as part of the Spring Context
  • applying the new todo URL to our application context as a property

Writing Integration Tests with WireMock and JUnit 5

We're registering the WireMockInitializer using the @ContextConfiguration for our test class. Within the actual integration test, we can now inject the WireMockServer and stub the HTTP response for each test case:

With .resetAll() we're resetting the WireMockServer to its default settings and remove all HTTP stubs. As we're sharing the same WireMock instance for all integration tests that use this Spring TestContext, we need a clean setup for each test.

Spring Boot auto-configures the WebTestClient for us once we use @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT). With this client, we can access our own REST API, which internally fetches data from the placeholder API (i.e., WireMock during the test) and verify the response afterward.

As we're in full control of the HTTP response of the remote system, we can also test non-happy-paths to understand and verify how our application reacts:

The test above simulates an HTTP 403 response for our placeholder API. As we don't have any exception handling in place for our demo application, we're propagating the exception. With WireMock, we can even test slow responses by specifying a delay using .withFixedDely():

You can find the source code for this example on GitHub.

For more practical hands-on testing recipes for writing unit, integration, and end-to-end tests for Spring Boot Applications, consider enrolling for the Testing Spring Boot Applications Masterclass.

PS: WireMock is also introduced in further detail as part of the Java Testing Toolbox.

Have fun writing integration tests with Spring Boot, WireMock, and JUnit 5 with this recipe,

Phil

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

    Join Our Mailing List To Get 3x Free Cheat Sheets

    Free Java Cheat Sheets
    >