Record & Replay
The record and replay pattern is at the heart of MockMaster. It allows you to capture real API responses and replay them deterministically in your tests.
Overview
The record and replay pattern enables you to:
- Record real API responses during development or from production
- Store them as JSON files in your repository
- Replay them in tests for deterministic, fast testing
This approach ensures your tests are:
- Fast - No network calls required
- Reliable - Same responses every time
- Offline-capable - Work without internet
- Version-controlled - Scenarios are committed with your code
Creating Recordings
A recording captures a single HTTP request and its corresponding response:
import { createRecording } from '@mockmaster/msw-adapter'
const recording = createRecording(
// Request
{
method: 'GET',
url: 'https://api.example.com/users/123',
path: '/users/123',
timestamp: Date.now()
},
// Response
{
status: 200,
body: {
id: 123,
name: 'John Doe',
email: 'john@example.com'
},
headers: {
'content-type': 'application/json'
},
timestamp: Date.now()
}
)Recording Structure
A recording consists of two parts:
Request:
method- HTTP method (GET, POST, PUT, etc.)url- Full URL of the requestpath- Path portion of the URLtimestamp- When the request was madebody(optional) - Request bodyheaders(optional) - Request headers
Response:
status- HTTP status codebody- Response bodyheaders(optional) - Response headerstimestamp- When the response was received
Creating Replay Handlers
Once you have recordings, create a replay handler to use them in tests:
import {
createScenario,
addRecordingToScenario,
createReplayHandler
} from '@mockmaster/msw-adapter'
// Build scenario with recordings
let scenario = createScenario('user-api', 'User API responses')
scenario = addRecordingToScenario(scenario, recording)
// Create handler
const handler = createReplayHandler(scenario)
// Replay in tests
const response = handler({
method: 'GET',
path: '/users/123'
})
console.log(response.status) // 200
console.log(response.body) // { id: 123, name: 'John Doe', ... }Request Matching
The replay handler automatically matches incoming requests to recorded responses using:
Exact Method Matching
// Will match
handler({ method: 'GET', path: '/users' })
// Won't match (different method)
handler({ method: 'POST', path: '/users' })Path Matching
Supports exact paths, parameters, and wildcards:
// Exact path
handler({ method: 'GET', path: '/users' })
// Path with parameters
handler({ method: 'GET', path: '/users/123' })
// Wildcard
handler({ method: 'GET', path: '/api/anything/here' })No Match Found
If no recording matches the request, the handler returns undefined:
const response = handler({ method: 'GET', path: '/not-recorded' })
if (!response) {
console.log('No recording found for this request')
}Multiple Recordings
A scenario can contain multiple recordings for different endpoints:
import { createScenario, addRecordingToScenario } from '@mockmaster/msw-adapter'
let scenario = createScenario('user-api', 'Complete user API')
// Add GET /users
const listUsersRecording = createRecording(
{ method: 'GET', url: 'https://api.example.com/users', path: '/users', timestamp: Date.now() },
{ status: 200, body: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }], timestamp: Date.now() }
)
scenario = addRecordingToScenario(scenario, listUsersRecording)
// Add GET /users/:id
const getUserRecording = createRecording(
{ method: 'GET', url: 'https://api.example.com/users/1', path: '/users/:id', timestamp: Date.now() },
{ status: 200, body: { id: 1, name: 'Alice', email: 'alice@example.com' }, timestamp: Date.now() }
)
scenario = addRecordingToScenario(scenario, getUserRecording)
// Add POST /users
const createUserRecording = createRecording(
{
method: 'POST',
url: 'https://api.example.com/users',
path: '/users',
body: { name: 'Charlie', email: 'charlie@example.com' },
timestamp: Date.now()
},
{ status: 201, body: { id: 3, name: 'Charlie', email: 'charlie@example.com' }, timestamp: Date.now() }
)
scenario = addRecordingToScenario(scenario, createUserRecording)
// Use all recordings
const handler = createReplayHandler(scenario)
handler({ method: 'GET', path: '/users' }) // Returns list
handler({ method: 'GET', path: '/users/1' }) // Returns Alice
handler({ method: 'POST', path: '/users' }) // Returns new userType Safety
MockMaster supports TypeScript generics for type-safe recordings:
interface User {
id: number
name: string
email: string
}
const recording = createRecording<User>(
{
method: 'GET',
url: 'https://api.example.com/users/123',
path: '/users/:id',
timestamp: Date.now()
},
{
status: 200,
body: {
id: 123,
name: 'John Doe',
email: 'john@example.com'
},
timestamp: Date.now()
}
)
// TypeScript knows response.body is of type User
const handler = createReplayHandler(scenario)
const response = handler({ method: 'GET', path: '/users/123' })
if (response) {
const user: User = response.body // Type-safe!
}Best Practices
1. Record Real Responses
When possible, record actual API responses from your backend or external APIs. This ensures your tests match real-world behavior.
2. Commit Recordings
Store recordings in your repository so all developers and CI/CD pipelines use the same data.
3. Organize by Feature
Create separate scenarios for different features or user flows:
scenarios/
├── auth/
│ ├── login-success.json
│ ├── login-failure.json
│ └── logout.json
├── users/
│ ├── list-users.json
│ └── get-user.json
└── products/
└── list-products.json4. Update When APIs Change
When your API changes, update the recordings to match. This keeps your tests in sync with reality.
5. Test Error Cases
Don’t just record success cases. Record errors too:
// 404 Not Found
const notFoundRecording = createRecording(
{ method: 'GET', url: 'https://api.example.com/users/999', path: '/users/999', timestamp: Date.now() },
{
status: 404,
body: { error: 'User not found', resourceId: '999' },
timestamp: Date.now()
}
)
// 500 Internal Server Error
const serverErrorRecording = createRecording(
{ method: 'GET', url: 'https://api.example.com/data', path: '/data', timestamp: Date.now() },
{
status: 500,
body: { error: 'Internal server error', errorId: 'err_123' },
timestamp: Date.now()
}
)Next Steps
- Learn about Scenarios & Recordings organization
- Explore OpenAPI Integration for automatic recording generation
- See Examples for complete use cases