Using WireMock for Integration Testing of Spring RestTemplate Services
This article explains how to write integration tests for Spring services that call external APIs by using WireMock to mock HTTP responses, handle dynamic ports, simulate error and timeout scenarios, and adjust exception handling in RestTemplate calls.
Whether you follow the traditional testing pyramid or a newer "testing honeycomb" approach, integration tests should be added at some point in the development process, especially when your service calls external APIs. This article shows how to mock those external calls with WireMock.
ChuckNorrisService is a simple Spring @Service that retrieves a fact via @Service public class ChuckNorrisService{ ... public ChuckNorrisFact retrieveFact() { ResponseEntity response = restTemplate.getForEntity(url, ChuckNorrisFactResponse.class); return Optional.ofNullable(response.getBody()).map(ChuckNorrisFactResponse::getFact).orElse(BACKUP_FACT); } ... }
A typical unit test mocks RestTemplate to return a successful response, but you also need a test that simulates HTTP error codes (4xx/5xx). The naive test uses a mocked RestTemplate that returns a ResponseEntity with a 503 status, expecting the service to fall back to a backup fact.
In reality, RestTemplate#getForEntity throws a RestClientException (or a subclass such as HttpStatusCodeException ) for error responses, so the mocked ResponseEntity is never returned. This discrepancy motivates the use of WireMock.
WireMock to the rescue starts a mock HTTP server that can be programmed to return specific responses. For JUnit 4 you can use a @ClassRule public static WireMockRule wireMockRule = new WireMockRule(); which starts and stops the server automatically.
Typical WireMock configuration methods include:
public void configureWireMockForOkResponse(ChuckNorrisFact fact) throws JsonProcessingException { ChuckNorrisFactResponse chuckNorrisFactResponse = new ChuckNorrisFactResponse("success", fact); stubFor(get(urlEqualTo("/jokes/random")) .willReturn(okJson(OBJECT_MAPPER.writeValueAsString(chuckNorrisFactResponse)))); }
and for error responses:
private void configureWireMockForErrorResponse() { stubFor(get(urlEqualTo("/jokes/random")) .willReturn(serverError())); }
Because RestTemplate throws exceptions on error status, the service method is updated to catch them:
public ChuckNorrisFact retrieveFact() { try { ResponseEntity response = restTemplate.getForEntity(url, ChuckNorrisFactResponse.class); return Optional.ofNullable(response.getBody()).map(ChuckNorrisFactResponse::getFact).orElse(BACKUP_FACT); } catch (HttpStatusCodeException e) { return BACKUP_FACT; } }
Dynamic ports are handled by an ApplicationContextInitializer that injects the randomly assigned WireMock port into the Spring context via ${wiremock.port} , allowing tests to run on cloud providers where fixed ports may be unavailable.
Timeout testing is demonstrated by configuring WireMock to delay responses:
private void configureWireMockForSlowResponse() throws JsonProcessingException { ChuckNorrisFactResponse chuckNorrisFactResponse = new ChuckNorrisFactResponse("success", new ChuckNorrisFact(1L, "")); stubFor(get(urlEqualTo("/jokes/random")) .willReturn( okJson(OBJECT_MAPPER.writeValueAsString(chuckNorrisFactResponse)) .withFixedDelay((int) Duration.ofSeconds(10L).toMillis()))); }
When a timeout occurs, RestTemplate throws a ResourceAccessException , so the catch block is broadened to handle RestClientException (the common superclass of both timeout and HTTP status exceptions).
In summary, the article demonstrates the importance of integration testing and shows how WireMock can be used to mock successful, error, and slow responses for Spring services, including handling dynamic ports and proper exception handling.
FunTester
10k followers, 1k articles | completely useless
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.