Modern applications heavily depend on REST APIs—both internal and external. Testing these APIs in isolation is challenging: you need predictable responses, controlled environments, and realistic infrastructure simulation. That’s where Testcontainers and WireMock shine together.
- WireMock simulates REST APIs, providing mock responses, error scenarios, and latency testing.
- Testcontainers spins up real, containerized services like databases, message brokers, and even WireMock itself for integration tests.
Together, they allow developers to test API clients and microservices in production-like conditions without unstable dependencies.
What Are Testcontainers and WireMock?
Testcontainers
Testcontainers is a Java library that provides lightweight, disposable containers for testing. It integrates with JUnit 5, enabling you to start and stop services dynamically in your test lifecycle.
WireMock
WireMock is a tool for mocking HTTP APIs. You can simulate endpoints, return custom responses, test error handling, and inject network delays—ideal for testing external API integrations.
Why Use Testcontainers with WireMock?
- Isolation: Test against a containerized WireMock instance without interfering with local or staging APIs.
- Reliability: Ensure consistent test outcomes independent of flaky external APIs.
- Flexibility: Simulate edge cases like 500 errors or timeouts.
- CI/CD Integration: Works seamlessly in Jenkins, GitHub Actions, or any CI pipeline.
Real-world scenario: You’re building a microservice that calls a payment API. Using WireMock inside Testcontainers, you can test both happy paths and failure paths without hitting the real payment gateway.
Setting Up Dependencies
Add the following dependencies in your Maven pom.xml
:
<dependencies>
<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<!-- Testcontainers -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mockserver</artifactId>
<version>1.19.0</version>
<scope>test</scope>
</dependency>
<!-- WireMock -->
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.35.0</version>
<scope>test</scope>
</dependency>
</dependencies>
And enable JUnit 5 in the maven-surefire-plugin
.
Example: Running WireMock with Testcontainers
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
@Testcontainers
class WireMockTest {
@Container
GenericContainer<?> wiremock = new GenericContainer<>("wiremock/wiremock:2.35.0")
.withExposedPorts(8080);
@Test
void testWireMockEndpoint() {
String baseUrl = "http://" + wiremock.getHost() + ":" + wiremock.getMappedPort(8080);
// WireMock admin API to register a stub
given()
.baseUri(baseUrl)
.contentType("application/json")
.body("{ "request": { "method": "GET", "url": "/hello" }, " +
""response": { "status": 200, "body": "Hello World!" }}")
.post("/__admin/mappings")
.then()
.statusCode(201);
// Test the stubbed endpoint
given()
.baseUri(baseUrl)
.get("/hello")
.then()
.statusCode(200)
.body(equalTo("Hello World!"));
}
}
Explanation:
- A WireMock container is started with Testcontainers.
- We use the WireMock Admin API to register a
/hello
stub. - Finally, we test the endpoint with
RestAssured
.
Advanced Usage Scenarios
Simulating Latency and Failures
{
"request": { "method": "GET", "url": "/delayed" },
"response": { "status": 200, "fixedDelayMilliseconds": 3000, "body": "Slow Response" }
}
This allows you to test timeout handling in your HTTP client.
Dynamic Ports in CI/CD
Testcontainers maps container ports to random host ports, ensuring no conflicts in parallel builds.
Combining with Databases
You can spin up PostgreSQL or MySQL containers alongside WireMock to test full request → DB → response workflows.
Best Practices
- Always clean up stubs between tests to avoid cross-test pollution.
- Use Testcontainers reusable mode to speed up builds.
- Organize stubs in JSON files for readability.
- Apply contract testing (e.g., Pact) alongside WireMock for microservices.
Version Tracker
- JUnit 4 → JUnit 5 transition introduced
@ExtendWith
and Testcontainers JUnit 5 module. - Mockito updates enabled mocking static/final methods (sometimes paired with WireMock for hybrid strategies).
- Testcontainers ecosystem expanded with modules for Kafka, RabbitMQ, LocalStack, and custom containers.
Conclusion and Key Takeaways
- WireMock: Simulates REST APIs reliably.
- Testcontainers: Runs WireMock (and other services) in isolated containers.
- Together: Enable robust, production-like API integration tests.
Key benefits: isolation, flexibility, CI/CD readiness, and reliability.
FAQ
1. What is the difference between unit and integration tests?
Unit tests check isolated logic, while integration tests validate how components work together.
2. Can I mock static methods with Mockito instead of WireMock?
Yes, Mockito can mock static methods, but WireMock is better for simulating external APIs.
3. How can Testcontainers help in CI/CD pipelines?
It runs real dependencies in Docker containers, ensuring consistent tests across developer laptops and CI servers.
4. Is WireMock production-safe?
No, it’s meant for testing only, not as a production proxy.
5. How do I handle flaky tests with Testcontainers?
Enable reusable containers and retry logic in tests.
6. What’s the difference between Testcontainers and MockServer?
MockServer is another HTTP mocking tool, while WireMock is more feature-rich for REST APIs.
7. Can I run WireMock alongside a database in Testcontainers?
Yes, you can spin up multiple containers in the same test lifecycle.
8. How do I simulate 500 errors with WireMock?
Define a stub with "status": 500
in the response.
9. How can I integrate with Spring Boot tests?
Use @Testcontainers
and inject the container’s URL into your Spring test configuration.
10. What are alternatives to WireMock?
MockServer, Hoverfly, or custom stubs—but WireMock is the most widely used.