Mock Servers for API Testing
In this tutorial, you will learn about Mock Servers for API Testing. We cover key concepts, practical examples, and best practices to help you master this topic.
Mock servers simulate API behavior for testing without real backends. WireMock and Pretender provide HTTP mock servers with request matching, response templating, and stateful behavior. They enable isolated testing of services with external dependencies.
What You'll Learn
- WireMock stub mapping and verification
- Pretender for browser-side mocking
- Request matching by URL, headers, body
- Response templating with dynamic data
- Record-playback patterns
Why It Matters
Mock servers decouple tests from external dependencies. They make tests faster, more reliable, and able to simulate edge cases that are hard to reproduce with real services.
Real-World Use
WireMock is used by Spring Boot projects for integration tests. Pretender is used in Ember.js testing. Many microservice test suites use WireMock for dependency isolation.
flowchart LR
Test[Integration Test] --> Service[Service Under Test]
Service --> Mock[Mock Server]
Mock --> Stub[Stub Mapping]
Mock --> Matcher[Request Matching]
Mock --> Response[Response Generation]
Mock --> Verify[Verification]
Teacher Mindset
Mock servers replace real dependencies in tests. Define stubs for expected requests. Verify that the expected requests were made. Use response templating for dynamic data.
Code Examples
// Example 1: WireMock stub definition
import static com.github.tomakehurst.wiremock.client.WireMock.*;
@Test
public void testGetUser() {
stubFor(get(urlEqualTo("/api/users/1"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"id\":1,\"name\":\"Alice\",\"email\":\"alice@test.com\"}")));
// Service under test calls http://localhost:8080/api/users/1
User user = userService.getUser(1);
assertThat(user.getName()).isEqualTo("Alice");
}
// Example 2: WireMock with request matching and verification
@Test
public void testCreateUser() {
stubFor(post(urlEqualTo("/api/users"))
.withRequestBody(matchingJsonPath("$.name"))
.withRequestBody(matchingJsonPath("$.email"))
.willReturn(aResponse()
.withStatus(201)
.withBody("{\"id\":2,\"name\":\"Bob\"}")));
userService.createUser("Bob", "bob@test.com");
verify(postRequestedFor(urlEqualTo("/api/users"))
.withRequestBody(containing("Bob")));
}
// Example 3: Pretender for browser mocking
import Pretender from 'pretender';
const server = new Pretender();
server.get('/api/users', () => {
return [200, { 'Content-Type': 'application/json' },
JSON.stringify({ users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]})
];
});
server.post('/api/users', (request) => {
const body = JSON.parse(request.requestBody);
return [201, { 'Content-Type': 'application/json' },
JSON.stringify({ id: 3, name: body.name })
];
});
// After test
server.shutdown();
Common Mistakes
- Making mocks too complex, testing the mock instead of the system
- Not verifying that expected requests were actually made
- Using mocks for services where contract tests are more appropriate
- Hardcoding response data instead of using response templating
- Forgetting to reset mock state between tests
Practice
- Create a WireMock stub for a GET endpoint with JSON response.
- Add request matching by header and query parameter.
- Use response templating to return dynamic IDs.
- Verify that the service made the expected requests.
- Challenge: Implement a stateful mock that returns different responses on subsequent calls.
FAQ
Mini Project
Set up a WireMock server for an external payment API. Create stubs for successful payment, declined card, insufficient funds, and timeout scenarios. Write integration tests that use each stub and verify the system behavior.
What's Next
Next, you will learn about generating test data with Faker for realistic API tests.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro