
1. Introduction
Testing external dependencies such as REST APIs can be challenging when developing web applications. Making network calls is slow and unreliable, as third-party services might be unavailable or return unexpected data. We must identify a robust method for simulating external services to guarantee consistent and dependable application testing. This is where WireMock comes in.
WireMock is a powerful HTTP mock server that allows us to stub and verify HTTP requests. It provides a controlled test environment, ensuring our integration tests are fast, repeatable, and independent of external systems.
In this tutorial, we’ll explore how to integrate WireMock into a Spring Boot project and use it to write comprehensive tests.
2. Maven Dependency
To use WireMock with Spring Boot, we need to include the wiremock-spring-boot dependency in our pom.xml:
<dependency>
<groupId>org.wiremock.integrations</groupId>
<artifactId>wiremock-spring-boot</artifactId>
<version>3.9.0</version>
<scope>test</scope>
</dependency>
This dependency provides seamless integration between WireMock and Spring Boot’s testing framework.
3. Writing a Basic WireMock Test
Before tackling more complex scenarios, we’ll begin by writing a simple WireMock test. We have to guarantee that our Spring Boot application can interact correctly with an external API. By using @SpringBootTest and @EnableWireMock annotations, WireMock is enabled in our testing environment. Then, we can define a simple test case to verify API behavior:
@SpringBootTest(classes = SimpleWiremockTest.AppConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@EnableWireMock
class SimpleWiremockTest {
@Value("${wiremock.server.baseUrl}")
private String wireMockUrl;
@Autowired
private TestRestTemplate restTemplate;
@Test
void givenWireMockStub_whenGetPing_thenReturnsPong() {
stubFor(get("/ping").willReturn(ok("pong")));
ResponseEntity<String> response = restTemplate.getForEntity(wireMockUrl + "/ping", String.class);
Assertions.assertEquals("pong", response.getBody());
}
@SpringBootApplication
static class AppConfiguration {}
}
In this test, we use the @EnableWireMock annotation to start an embedded WireMock server for the test environment. The @Value(“${wiremock.server.baseUrl}”) annotation retrieves the base URL for WireMock from the property file. The test method stubs an endpoint /ping to return “pong” with an HTTP 200 status code. We then make an actual HTTP request using TestRestTemplate and verify that the response body matches the expected value. This ensures that our application correctly communicates with the mocked external service.
4. Making the Test More Complex
Now that we have a basic test, let’s extend our example to mock a REST API that returns JSON responses and handles various status codes. This will help us verify how our application processes different API behaviors.
4.1. Stubbing a JSON Response
A common scenario in REST APIs is returning structured JSON responses. We can also simulate this case using Wiremock stubs:
@Test
void givenWireMockStub_whenGetGreeting_thenReturnsMockedJsonResponse() {
String mockResponse = "{\"message\": \"Hello, Baeldung!\"}";
stubFor(get("/api/greeting")
.willReturn(okJson(mockResponse)));
ResponseEntity<String> response = restTemplate.getForEntity(wireMockUrl + "/api/greeting", String.class);
Assertions.assertEquals(HttpStatus.OK, response.getStatusCode());
Assertions.assertEquals(mockResponse, response.getBody());
}
In this test, we stub a GET request to /api/greeting that returns a JSON response containing a greeting message. We then request the WireMock server to verify that the response status code is 200 OK and the body matches the expected JSON structure.
4.2. Simulating an Error Response
We all know that things don’t always go as they should, especially in web development, and some external calls can return errors. To be prepared for this, we can also simulate error messages to prepare our application to respond appropriately to this:
@Test
void givenWireMockStub_whenGetUnknownResource_thenReturnsNotFound() {
stubFor(get("/api/unknown").willReturn(aResponse().withStatus(404)));
ResponseEntity<String> response = restTemplate.getForEntity(wireMockUrl + "/api/unknown", String.class);
Assertions.assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
}
5. Injecting WireMock Server
In more complex scenarios, we may need to manage multiple WireMock instances or configure them with specific settings. WireMock allows us to inject and configure multiple WireMock servers using the @InjectWireMock annotations. This is particularly useful when our application interacts with numerous external services, and we want to mock each one independently.
5.1. Injecting a Single WireMock Server
Let’s start by injecting a single WireMock server into our test class. This method is helpful when mocking a single external service:
@SpringBootTest(classes = SimpleWiremockTest.AppConfiguration.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@EnableWireMock({
@ConfigureWireMock(name = "user-service", port = 8081),
})
public class InjectedWiremockTest {
@InjectWireMock("user-service")
WireMockServer mockUserService;
@Autowired
private TestRestTemplate restTemplate;
@Test
void givenEmptyUserList_whenFetchingUsers_thenReturnsEmptyList() {
mockUserService.stubFor(get("/users").willReturn(okJson("[]")));
ResponseEntity<String> response = restTemplate.getForEntity(
"http://localhost:8081/users",
String.class);
Assertions.assertEquals(HttpStatus.OK, response.getStatusCode());
Assertions.assertEquals("[]", response.getBody());
}
}
Unlike the previous approach, where WireMock was enabled at the test class level using @EnableWireMock without explicit injection, this method allows for more granular control by injecting a named WireMock server instance. The @ConfigureWireMock annotation explicitly defines the WireMock instance’s name and port, making it easy to manage multiple external services within different test cases.
@InjectWireMock(“user-service”) allows us to gain direct access to the WireMockServer instance to configure and manage its behavior dynamically within our test methods.
5.2. Injecting Multiple WireMock Servers
In cases where our application interacts with multiple external services, we might need to mock multiple APIs using separate WireMock instances. WireMock allows us to configure and specify different names and ports for each instance:
@SpringBootTest(classes = SimpleWiremockTest.AppConfiguration.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@EnableWireMock({
@ConfigureWireMock(name = "user-service", port = 8081),
@ConfigureWireMock(name = "product-service", port = 8082)
})
public class InjectedWiremockTest {
@InjectWireMock("user-service")
WireMockServer mockUserService;
@InjectWireMock("product-service")
WireMockServer mockProductService;
@Autowired
private TestRestTemplate restTemplate;
@Test
void givenUserAndProductLists_whenFetchingUsersAndProducts_thenReturnsMockedData() {
mockUserService.stubFor(get("/users")
.willReturn(okJson("[{\"id\": 1, \"name\": \"John\"}]")));
mockProductService.stubFor(get("/products")
.willReturn(okJson("[{\"id\": 101, \"name\": \"Laptop\"}]")));
ResponseEntity<String> userResponse = restTemplate
.getForEntity("http://localhost:8081/users", String.class);
ResponseEntity<String> productResponse = restTemplate
.getForEntity("http://localhost:8082/products", String.class);
Assertions.assertEquals(HttpStatus.OK, userResponse.getStatusCode());
Assertions.assertEquals("[{\"id\": 1, \"name\": \"John\"}]", userResponse.getBody());
Assertions.assertEquals(HttpStatus.OK, productResponse.getStatusCode());
Assertions.assertEquals("[{\"id\": 101, \"name\": \"Laptop\"}]", productResponse.getBody());
}
}
We isolated the services, ensuring that changes to one mock server do not interfere with others. By injecting multiple WireMock instances, we can fully simulate complex service interactions, making our tests more accurate and reliable. This method is particularly beneficial in microservices architectures, where different components communicate with various external services.
6. Conclusion
WireMock is a powerful tool for testing external dependencies in a Spring Boot application. In this article, we created reliable, repeatable, and independent tests without depending on actual third-party services. We started with a simple test and evolved it into more advanced scenarios, including injecting multiple WireMock servers.
With these techniques, we can ensure that our applications handle external API responses correctly, whether they return expected data or errors. The implementation of all the examples and code snippets can be found over on GitHub.
The post Integrating WireMock with Spring Boot first appeared on Baeldung.