In modern software development, external APIs play a critical role in providing functionality, data, and integration with third-party services. The application that I’m currently working on has a third-party service that provides authentication services. It integrates with another service for analytics, and yet another one for logging. We also rely on third-party services for contextual help/on-boarding and feature-flagging. This app also relies on APIs built by other teams within the organization for one service or another. The scope of what my team is building is a small slice of the overall picture. I had to stop and think about these dependencies when I recently undertook the project of writing automated integration/e2e tests for this app. How do I test the “app” with all these other services in the mix? Testing against live APIs can be challenging—external dependencies might be unreliable, rate-limited, or simply unavailable during development. This is how I came across WireMock, a powerful tool for simulating HTTP APIs.
What is WireMock?
WireMock is an open-source tool that allows developers to mock HTTP APIs by simulating HTTP endpoints. It mimics the behavior of real services, enabling developers to create realistic and customizable responses to HTTP requests without relying on the actual external APIs.
Key features of WireMock include:
- Request Matching – Define specific request conditions and simulate different responses.
- Stubbed Responses – Return pre-configured responses for particular requests, enabling predictable testing.
- Fault Injection – Simulate timeouts, delays, and errors to test how the system handles failures.
- Scalability – Can be run locally, in Docker containers, or as part of CI/CD pipelines.
Why Use WireMock?
Isolation from External Services
When testing, relying on external APIs can introduce flakiness. If the API goes down or has inconsistent response times, your tests may fail for reasons beyond your control. WireMock eliminates this issue by allowing you to simulate the API locally.
Faster and More Predictable Tests
WireMock enables you to simulate specific conditions consistently, ensuring that your tests are repeatable. This results in faster, more reliable automated testing without waiting for external API responses.
Simulating Edge Cases
Testing for rare conditions, such as 500 errors or slow responses, can be difficult with live APIs. WireMock allows you to create these scenarios on demand to see how your application handles them.
Cost Efficiency
Third-party APIs often have usage limits or costs associated with excessive requests. WireMock reduces the need to make live calls, saving on API expenses.
Setting Up WireMock
Let’s walk through how to get started with WireMock.
Installation
WireMock can be run as a standalone JAR file, via Docker, or embedded in Java or .NET applications.
Using Docker:
docker run -it -p 8080:8080 wiremock/wiremock
WireMock will be available at http://localhost:8080 by default.
Creating Mocks (Stubbing Responses)
WireMock allows you to define stubs using JSON files or programmatically in code.
Example: Basic Stub (JSON)
Create a file __files/get-user-response.json
for the mock response:
{ "id": 1, "name": "John Doe", "email": "john.doe@example.com" }
Define the stubbed endpoint in mappings/get-user.json
:
{ "request": { "method": "GET", "url": "/user/1" }, "response": { "status": 200, "bodyFileName": "get-user-response.json", "headers": { "Content-Type": "application/json" } } }
Now, a GET
request to /user/1
will return the mock response.
Simulating Delays and Errors
WireMock allows you to introduce delays or simulate faults:
Simulating a Slow Response
{ "request": { "method": "GET", "url": "/slow-service" }, "response": { "status": 200, "fixedDelayMilliseconds": 5000 } }
Returning a 500 Error
{ "request": { "method": "GET", "url": "/fail" }, "response": { "status": 500, "body": "Internal Server Error" } }
Integration Tests
My whole exploration of WireMock came from the need for writing Integration Tests. I was already doing integration testing in a dockerized environment and thus, WireMock became a natural fit. WireMock is just another service in my Docker Compose file. However, what allowed for a seamless integration were some good development practices that our team followed. Namely, the URLs of our external services are all specified within configuration files and not within the code itself. This allows us to create new configuration values that target a WireMock instance, instead of the real thing, without having to make any code changes. If your external service URLs are embedded directly on code, first do a refactor to get them into external configurations files (or systems) that can be injected in upon runtime.
Best Practices for Using WireMock
- Isolate Tests – Use WireMock in isolated test environments to prevent conflicts with live services.
- Version Control Stubs – Store your WireMock stubs in version control to ensure consistency across environments.
- Simulate Realistic Scenarios – Create stubs that closely resemble real API responses, including errors and edge cases.
- Use Dynamic Port Binding – When running tests in parallel, allow WireMock to bind dynamically to avoid port conflicts.
Conclusion
WireMock is an invaluable tool for developers seeking to create reliable, scalable, and predictable tests involving HTTP services. By simulating APIs, WireMock ensures that external dependencies don’t become bottlenecks or sources of instability. Whether you are developing a microservices architecture or testing third-party integrations, WireMock can help you achieve robust testing with greater efficiency.
If you haven’t used WireMock yet, give it a try in your next project!