Basic Scenario
A complete, step-by-step guide to creating your first MockMaster scenario from scratch. This tutorial will walk you through every step of the process.
Overview
In this tutorial, you’ll learn to:
- Create a basic scenario from scratch
- Add request/response recordings
- Save the scenario to disk
- Load and use it in tests
Time to complete: 10 minutes
Step 1: Install Packages
First, install the required MockMaster packages:
npm install @mockmaster/msw-adapter @mockmaster/cliWhat these packages do:
@mockmaster/msw-adapter- Create scenarios and recordings@mockmaster/cli- File operations (save/load scenarios)
Step 2: Create Your First Scenario
Create a new file create-scenario.ts:
import { createScenario } from '@mockmaster/msw-adapter'
// Create an empty scenario
const scenario = createScenario(
'user-api', // Scenario name
'Basic user API endpoints' // Description
)
console.log('Created scenario:', scenario.name)
console.log('Recordings:', scenario.recordings.length) // 0What happens here:
createScenario()creates an empty scenario container- The scenario has a name, description, and empty recordings array
- Timestamps are automatically added
Step 3: Create a Recording
A recording captures one HTTP request and its response:
import { createRecording } from '@mockmaster/msw-adapter'
const getUsersRecording = createRecording(
// Request details
{
method: 'GET',
url: 'https://api.example.com/users',
path: '/users',
timestamp: Date.now()
},
// Response details
{
status: 200,
body: [
{ id: 1, name: 'Alice Johnson', email: 'alice@example.com' },
{ id: 2, name: 'Bob Smith', email: 'bob@example.com' }
],
headers: {
'content-type': 'application/json'
},
timestamp: Date.now()
}
)
console.log('Created recording for:', getUsersRecording.request.method, getUsersRecording.request.path)Key points:
- Request - Describes the HTTP request (method, URL, path)
- Response - Describes what the API returns (status, body, headers)
- Timestamps - Track when request/response occurred
Step 4: Add Recording to Scenario
Add the recording to your scenario:
import { addRecordingToScenario } from '@mockmaster/msw-adapter'
// Start with empty scenario
let scenario = createScenario('user-api', 'Basic user API endpoints')
// Add the recording
scenario = addRecordingToScenario(scenario, getUsersRecording)
console.log('Recordings in scenario:', scenario.recordings.length) // 1Important: addRecordingToScenario returns a new scenario object (immutable pattern). Always reassign the result.
Step 5: Add More Recordings
Let’s add a recording for getting a single user:
// Recording for GET /users/:id
const getUserRecording = createRecording(
{
method: 'GET',
url: 'https://api.example.com/users/1',
path: '/users/:id', // Use :id for path parameter
timestamp: Date.now()
},
{
status: 200,
body: {
id: 1,
name: 'Alice Johnson',
email: 'alice@example.com',
bio: 'Software engineer and open source contributor'
},
timestamp: Date.now()
}
)
// Add to scenario
scenario = addRecordingToScenario(scenario, getUserRecording)
console.log('Total recordings:', scenario.recordings.length) // 2Step 6: Save Scenario to Disk
Save your scenario as a JSON file:
import { writeScenario } from '@mockmaster/cli'
// Save to ./scenarios directory
await writeScenario('./scenarios', scenario)
console.log('✓ Scenario saved to ./scenarios/user-api.json')File structure created:
scenarios/
└── user-api.jsonThe JSON file looks like:
{
"name": "user-api",
"description": "Basic user API endpoints",
"recordings": [
{
"request": {
"method": "GET",
"url": "https://api.example.com/users",
"path": "/users",
"timestamp": 1234567890
},
"response": {
"status": 200,
"body": [...],
"timestamp": 1234567890
}
}
],
"createdAt": 1234567890,
"updatedAt": 1234567890
}Commit this file to version control so your team uses the same mock data.
Step 7: Use in Tests
Now use your scenario in tests:
import { describe, it, expect, beforeEach } from 'vitest'
import { readScenario } from '@mockmaster/cli'
import { createReplayHandler } from '@mockmaster/msw-adapter'
describe('User API', () => {
let handler: ReturnType<typeof createReplayHandler>
beforeEach(async () => {
// Load scenario from disk
const scenario = await readScenario('./scenarios', 'user-api')
// Create replay handler
handler = createReplayHandler(scenario)
})
it('fetches list of users', () => {
// Make request
const response = handler({ method: 'GET', path: '/users' })
// Verify response
expect(response).toBeDefined()
expect(response!.status).toBe(200)
expect(response!.body).toHaveLength(2)
expect(response!.body[0].name).toBe('Alice Johnson')
})
it('fetches single user by ID', () => {
const response = handler({ method: 'GET', path: '/users/1' })
expect(response).toBeDefined()
expect(response!.status).toBe(200)
expect(response!.body.id).toBe(1)
expect(response!.body.name).toBe('Alice Johnson')
})
it('returns undefined for unmatched requests', () => {
const response = handler({ method: 'GET', path: '/posts' })
expect(response).toBeUndefined()
})
})What’s happening:
- Load scenario from disk in
beforeEach - Create a replay handler
- Use handler to get mock responses
- Test behaves exactly the same every time
Complete Example
Here’s the complete code in one file:
import {
createScenario,
createRecording,
addRecordingToScenario,
createReplayHandler
} from '@mockmaster/msw-adapter'
import { writeScenario, readScenario } from '@mockmaster/cli'
async function main() {
console.log('Creating MockMaster scenario...\n')
// 1. Create scenario
let scenario = createScenario('user-api', 'Basic user API endpoints')
console.log('✓ Created scenario:', scenario.name)
// 2. Add GET /users recording
const getUsersRecording = createRecording(
{ method: 'GET', url: 'https://api.example.com/users', path: '/users', timestamp: Date.now() },
{
status: 200,
body: [
{ id: 1, name: 'Alice Johnson', email: 'alice@example.com' },
{ id: 2, name: 'Bob Smith', email: 'bob@example.com' }
],
timestamp: Date.now()
}
)
scenario = addRecordingToScenario(scenario, getUsersRecording)
console.log('✓ Added GET /users recording')
// 3. Add GET /users/:id recording
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 Johnson', email: 'alice@example.com', bio: 'Software engineer' },
timestamp: Date.now()
}
)
scenario = addRecordingToScenario(scenario, getUserRecording)
console.log('✓ Added GET /users/:id recording')
// 4. Save to disk
await writeScenario('./scenarios', scenario)
console.log('✓ Saved to ./scenarios/user-api.json\n')
// 5. Load and test
console.log('Testing scenario...\n')
const loadedScenario = await readScenario('./scenarios', 'user-api')
const handler = createReplayHandler(loadedScenario)
// Test GET /users
const usersResponse = handler({ method: 'GET', path: '/users' })
console.log('GET /users ->', usersResponse?.status, `(${usersResponse?.body.length} users)`)
// Test GET /users/1
const userResponse = handler({ method: 'GET', path: '/users/1' })
console.log('GET /users/1 ->', userResponse?.status, `(${userResponse?.body.name})`)
// Test unmatched request
const notFoundResponse = handler({ method: 'GET', path: '/posts' })
console.log('GET /posts ->', notFoundResponse ? 'matched' : 'no match')
console.log('\n✓ Scenario created and tested successfully!')
}
main().catch(console.error)Run it:
npx tsx create-scenario.tsOutput:
Creating MockMaster scenario...
✓ Created scenario: user-api
✓ Added GET /users recording
✓ Added GET /users/:id recording
✓ Saved to ./scenarios/user-api.json
Testing scenario...
GET /users -> 200 (2 users)
GET /users/1 -> 200 (Alice Johnson)
GET /posts -> no match
✓ Scenario created and tested successfully!Common Patterns
Pattern 1: Multiple Endpoints
let scenario = createScenario('api', 'Complete API')
// Users
scenario = addRecordingToScenario(scenario, createRecording(...))
// Products
scenario = addRecordingToScenario(scenario, createRecording(...))
// Orders
scenario = addRecordingToScenario(scenario, createRecording(...))Pattern 2: Different HTTP Methods
// GET
const getRecording = createRecording(
{ method: 'GET', path: '/users', ... },
{ status: 200, body: [...] }
)
// POST
const postRecording = createRecording(
{ method: 'POST', path: '/users', body: { name: 'New User' }, ... },
{ status: 201, body: { id: 3, name: 'New User' } }
)
// DELETE
const deleteRecording = createRecording(
{ method: 'DELETE', path: '/users/1', ... },
{ status: 204, body: null }
)Pattern 3: Error Responses
const notFoundRecording = createRecording(
{ method: 'GET', path: '/users/999', timestamp: Date.now() },
{
status: 404,
body: { error: 'Not Found', message: 'User not found' },
timestamp: Date.now()
}
)Troubleshooting
Recording Not Matching
Problem: handler() returns undefined
Solution: Check that:
- Method matches exactly (case-sensitive)
- Path matches the pattern (use
:idfor parameters) - Recording was actually added to the scenario
// Debug mode
const response = handler({ method: 'GET', path: '/users' })
if (!response) {
console.log('Available recordings:')
scenario.recordings.forEach(r => {
console.log(` ${r.request.method} ${r.request.path}`)
})
}File Not Found
Problem: readScenario() throws error
Solution: Ensure:
- Directory exists:
mkdir -p scenarios - File was saved: Check
scenarios/user-api.jsonexists - Name matches: Use exact name (without
.json)
TypeScript Errors
Problem: Type errors with response body
Solution: Use generics:
interface User {
id: number
name: string
email: string
}
const recording = createRecording<User>(...)Next Steps
Now that you’ve created your first scenario:
- Add More Endpoints - Expand your scenario with more recordings
- Use in Real Tests - Integrate with Vitest or Jest
- Try OpenAPI - Generate scenarios from OpenAPI specs
- Explore Examples - Check out REST API and Authentication examples
Key Takeaways
- ✅ Scenarios are containers for recordings
- ✅ Recordings capture request/response pairs
- ✅ Save scenarios to disk for version control
- ✅ Replay handlers match requests and return responses
- ✅ Tests are deterministic and fast
- ✅ No real API calls in tests
Congratulations! You’ve successfully created your first MockMaster scenario.