ExamplesBasic Scenario

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:

  1. Create a basic scenario from scratch
  2. Add request/response recordings
  3. Save the scenario to disk
  4. 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/cli

What 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) // 0

What 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) // 1

Important: 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) // 2

Step 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.json

The 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:

  1. Load scenario from disk in beforeEach
  2. Create a replay handler
  3. Use handler to get mock responses
  4. 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.ts

Output:

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:

  1. Method matches exactly (case-sensitive)
  2. Path matches the pattern (use :id for parameters)
  3. 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:

  1. Directory exists: mkdir -p scenarios
  2. File was saved: Check scenarios/user-api.json exists
  3. 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:

  1. Add More Endpoints - Expand your scenario with more recordings
  2. Use in Real Tests - Integrate with Vitest or Jest
  3. Try OpenAPI - Generate scenarios from OpenAPI specs
  4. 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.