Are you having trouble writing tests for Service classes that call external REST APIs?
You can technically test them by mocking the HTTP client with Mockito, but there’s the issue that “no actual HTTP communication is happening.” When you want to verify timeout behavior or how the code responds to 5xx errors, Mockito alone just can’t handle it.
That’s where WireMock comes in. It lets you spin up an HTTP server locally and stub out external APIs at the HTTP level. This article walks through everything from setup to practical stub definitions.
The Difference Between Mockito and WireMock
Let’s get this straight first.
Mockito injects mocks at the class boundary. If you mock a RestTemplate Bean, you can control the return value of exchange(), but no actual HTTP socket communication occurs. Network-layer behaviors like connection timeouts or SSL errors cannot be reproduced.
WireMock starts a local HTTP server and returns arbitrary responses to requests on specified paths. Both RestTemplate and WebClient send “real HTTP requests,” so you can verify HTTP configuration mistakes and timeout behavior as well.
The decision is simple: “Do you want to test including the HTTP layer?” If unit testing with JUnit/Mockito is sufficient, choose Mockito. If you need to include actual communication via RestTemplate/WebClient, choose WireMock.
Adding Dependencies
The Correct Import Statement for org.wiremock.client.WireMock (for WireMock 3.x)
In WireMock 3.x (and wiremock-spring-boot 3.x), the package containing static methods like stubFor / get / urlEqualTo has changed. If you landed on this page from a search, check this first.
WireMock 3.x (recommended, new package)
import static org.wiremock.client.WireMock.*;
For individual imports, it looks like this.
import static org.wiremock.client.WireMock.stubFor;
import static org.wiremock.client.WireMock.get;
import static org.wiremock.client.WireMock.post;
import static org.wiremock.client.WireMock.urlEqualTo;
import static org.wiremock.client.WireMock.aResponse;
import static org.wiremock.client.WireMock.verify;
import static org.wiremock.client.WireMock.getRequestedFor;
import static org.wiremock.client.WireMock.equalTo;
WireMock 2.x / Old Package (retained for backward compatibility)
import static com.github.tomakehurst.wiremock.client.WireMock.*;
Differences Between Old and New Packages, and How to Choose
| Package | Supported Version | Use Case |
|---|---|---|
org.wiremock.client.WireMock | WireMock 3.x | Use this for new projects |
com.github.tomakehurst.wiremock.client.WireMock | WireMock 2.x onwards (still usable in 3.x for backward compatibility) | Grace period for migrating existing code |
If you have an old artifact like com.github.tomakehurst:wiremock-jre8 in your pom.xml / build.gradle, your IDE won’t resolve org.wiremock.client.WireMock even when auto-completing. First check that your dependency is org.wiremock.integrations:wiremock-spring-boot (or org.wiremock:wiremock-standalone).
Checklist When the IDE Can’t Resolve org.wiremock.client.WireMock
- Is your dependency
org.wiremock.integrations:wiremock-spring-boot:3.x? - Are any 2.x-series dependencies like
wiremock-jre8still lingering? (Mixing them will mess up import completion.) - Did you refresh the Maven/Gradle cache (
mvn -U/./gradlew --refresh-dependencies)? - Is your test class under the
testscope?
wiremock-spring-boot comes with auto-configuration for Spring Boot, keeping setup to a minimum.
For Maven
<dependency>
<groupId>org.wiremock.integrations</groupId>
<artifactId>wiremock-spring-boot</artifactId>
<!-- Check the latest version on Maven Central (org.wiremock.integrations:wiremock-spring-boot) -->
<version>3.1.0</version>
<scope>test</scope>
</dependency>
For Gradle
// Check the latest version on the wiremock-spring-boot GitHub Releases page
testImplementation 'org.wiremock.integrations:wiremock-spring-boot:3.1.0'
Note that com.github.tomakehurst:wiremock-jre8 is an artifact from the WireMock 2.x era, so avoid using it in new projects.
Setting Up with @SpringBootTest + @EnableWireMock
When using wiremock-spring-boot, the main configuration is combining @SpringBootTest with @EnableWireMock. The Spring context starts up, so you can inject your Service with @Autowired.
About imports: In WireMock 3.x, the package has been migrated to org.wiremock. In wiremock-spring-boot 3.x environments, org.wiremock.client.WireMock.* is the recommended import. The old package com.github.tomakehurst.wiremock.client.WireMock.* remains for backward compatibility, but use the new package to avoid confusion when cross-referencing the official documentation.
Also, when you use @EnableWireMock, stub definitions and request history are automatically reset for each test method. Stubs from previous tests won’t bleed into the next one.
import static org.wiremock.client.WireMock.*;
@SpringBootTest
@EnableWireMock
class UserServiceTest {
@Autowired
private UserService userService;
@Test
void getUser_success() {
stubFor(get(urlEqualTo("/api/users/1"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"id\": 1, \"name\": \"Taro\"}")));
User user = userService.findById(1L);
assertThat(user.getName()).isEqualTo("Taro");
}
}
There’s also @WireMockTest provided by the core WireMock library, but it doesn’t start the Spring context. @Autowired won’t work, and you’ll need to instantiate the Service manually with new. For tests that need the Spring Boot context, use @SpringBootTest + @EnableWireMock.
Pointing RestTemplate/WebClient Base URLs to WireMock
When you use @EnableWireMock, the wiremock.server.port property is automatically registered (this doesn’t happen with @WireMockTest alone). Just write this in application-test.properties.
api.base-url=http://localhost:${wiremock.server.port}
On the Service class side, implement it to receive the URL via @Value("${api.base-url}"). See also how to configure RestTemplate/WebClient.
Stub Definition Patterns: The Basics of stubFor
If you want to use a JSON file for the response body, withBodyFile is handy. The convention is to place files under src/test/resources/__files/.
// Place the JSON at src/test/resources/__files/responses/user.json
stubFor(get(urlEqualTo("/api/users/1"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBodyFile("responses/user.json")));
POST request stubs can be written the same way.
stubFor(post(urlEqualTo("/api/users"))
.willReturn(aResponse()
.withStatus(201)
.withHeader("Content-Type", "application/json")
.withBody("{\"id\": 2, \"name\": \"Jiro\"}")));
Reproducing Error Responses (4xx/5xx)
Getting a real API to return a 500 on demand is tough, right? With WireMock, it’s easy.
@Test
void getUser_throwsExceptionOnServerError() {
stubFor(get(urlEqualTo("/api/users/1"))
.willReturn(aResponse().withStatus(500)));
assertThrows(HttpServerErrorException.class, () -> {
userService.findById(1L);
});
}
RestTemplate throws HttpClientErrorException on 4xx and HttpServerErrorException on 5xx. If you’re using WebClient, verify the WebClientResponseException that you’re handling in onStatus.
Simulating Timeouts (withFixedDelay)
You can intentionally delay responses with withFixedDelay. Prepare a RestTemplate Bean with a short ReadTimeout for testing and pull it in with @Import.
import static org.wiremock.client.WireMock.*;
@SpringBootTest
@EnableWireMock
@Import(TimeoutTest.TimeoutConfig.class)
class TimeoutTest {
@Autowired
private UserService userService;
@Test
void getUser_throwsResourceAccessExceptionOnTimeout() {
stubFor(get(urlEqualTo("/api/users/1"))
.willReturn(aResponse()
.withStatus(200)
.withFixedDelay(3000))); // 3-second delay
assertThrows(ResourceAccessException.class, () -> {
userService.findById(1L);
});
}
@TestConfiguration
static class TimeoutConfig {
@Bean
@Primary
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.setReadTimeout(Duration.ofSeconds(1))
.build();
}
}
}
It’s also useful for verifying that retries or fallbacks kick in after a timeout. If your service is combined with Resilience4j’s circuit breaker, you can reproduce the delay with WireMock and test whether the circuit opens.
Verifying API Calls with verify
Beyond just returning responses with stubs, it’s important to verify that “the correct request is being sent.”
@Test
void getUser_callsCorrectEndpoint() {
stubFor(get(urlEqualTo("/api/users/1"))
.willReturn(aResponse().withStatus(200).withBody("{\"id\": 1}")));
userService.findById(1L);
verify(1, getRequestedFor(urlEqualTo("/api/users/1"))
.withHeader("Accept", equalTo("application/json")));
}
You can verify the body of a POST request with withRequestBody(containing("...")). If you care about the call count, use verify(exactly(1), ...) or verify(moreThan(0), ...).
Mockito vs. WireMock: Summary of When to Use Which
| Situation | Tool to Choose |
|---|---|
| Mocking internal dependencies not involving HTTP | Mockito |
| Including actual HTTP communication via RestTemplate/WebClient | WireMock |
| Reproducing timeouts or network errors | WireMock |
| DB integration tests | Testcontainers |
By combining with integration testing using Testcontainers, you can boost coverage with a setup where the DB is handled by Testcontainers and external HTTP by WireMock.
Conclusion
WireMock makes it easy to write external API stubs at the HTTP level. You can test not only the happy path but also error cases and timeouts, freeing you from flaky tests that depend on real APIs. Once you can verify the contents of requests with verify, testing external API integrations becomes far more thorough.