API Mocking for QA Testing
Test error responses, timeouts, and edge cases that are impossible to trigger with real APIs.
How do you test what happens when the payment gateway returns a 503? Or when the API takes 30 seconds to respond? With real APIs, you can't. With mocks, you control everything.
The Testing Challenge
QA engineers face a fundamental problem: real APIs don't fail on demand. You can't ask Stripe to return a "card declined" error. You can't make your database timeout for testing.
This leaves critical code paths untested:
- Error handling for 4xx and 5xx responses
- Timeout and retry logic
- Rate limiting behavior
- Partial failure scenarios
- Edge cases with unusual data
Common Test Scenarios
Error Responses
Test 400, 401, 403, 404, 500, 502, 503 errors
Slow Responses
Test loading states, timeouts, and retry logic
Rate Limiting
Test 429 responses and backoff behavior
Auth Failures
Test expired tokens and permission errors
Example: Testing Error Scenarios
404 Not Found
{
"error": "Not Found",
"message": "User with ID 'nonexistent' does not exist",
"code": "USER_NOT_FOUND"
}500 Internal Server Error
{
"error": "Internal Server Error",
"message": "An unexpected error occurred",
"requestId": "{{$randomUUID}}"
}429 Rate Limited
{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Please retry after 60 seconds.",
"retryAfter": 60
}Testing Timeouts
MockStation lets you add response delays to test timeout handling:
- 2 second delay: Test loading indicators
- 10 second delay: Test patience thresholds
- 30 second delay: Test timeout logic
Does your app retry? Show an error message? Give up gracefully? Now you can verify.
Testing with Cypress
Here's how to use MockStation endpoints in Cypress tests:
describe('Error Handling', () => {
it('shows error message on 500', () => {
// Use MockStation endpoint that returns 500
cy.intercept('GET', '/api/users/*', {
statusCode: 500,
body: { error: 'Internal Server Error' }
});
cy.visit('/dashboard');
cy.contains('Something went wrong').should('be.visible');
cy.contains('Try again').should('be.visible');
});
it('handles timeout gracefully', () => {
cy.intercept('GET', '/api/users/*', {
delay: 35000, // 35 second delay
body: { user: {} }
});
cy.visit('/dashboard');
cy.contains('Request timed out', { timeout: 40000 });
});
});Testing with Playwright
import { test, expect } from '@playwright/test';
test('displays error on API failure', async ({ page }) => {
// Route to MockStation error endpoint
await page.route('**/api/users/**', route => {
route.fulfill({
status: 500,
body: JSON.stringify({ error: 'Server Error' })
});
});
await page.goto('/dashboard');
await expect(page.locator('.error-message')).toBeVisible();
});Edge Case Testing
Use Faker to generate edge case data:
- Empty arrays: Test empty state UI
- Very long strings: Test text overflow
- Special characters: Test encoding
- Null values: Test null handling
- Unicode: Test internationalization
{
"user": {
"name": "Müller-Schmidt O'Brien III",
"bio": "{{$faker.lorem.paragraphs 5}}",
"website": null,
"followers": 0,
"posts": []
}
}Deterministic Testing
Unlike real APIs, mocks are deterministic. The same request always returns the same response structure. This makes tests:
- Reproducible: Same result every time
- Fast: No network latency to real servers
- Reliable: No external dependencies
- Debuggable: You control every variable
Get Started
Start testing edge cases today:
- Create a free account
- Create mock endpoints for success and error scenarios
- Add response delays for timeout testing
- Use mock URLs in your test suite
Improve Your Test Coverage
Create deterministic mock APIs that let you test every scenario.