HTTP Recording

HTTP Recording

Spectest can record and replay the outbound HTTP requests made by a Node-based server under test (SUT). Spectest still sends real requests to your local API — recording only intercepts the calls your API makes to other services (a payments provider, a third-party API, etc.), so tests stay fast and deterministic without hand-rolled mocks.

This works by starting the SUT process with a recording preload so its fetch, http, https, and common Node HTTP library calls can be captured. Because instrumentation requires controlling process startup, recording is incompatible with runningServer: 'reuse' — Spectest must be the one that spawns the server.

Configuration

// spectest.config.js

export default {
  baseUrl: 'http://localhost:3000',
  startCmd: 'npm run start',
  runningServer: 'kill',
  recording: 'replay',
  recordingFile: '.spectest/cassette.json',
  missingRecordingBehavior: 'fail',
  recordingExcludeUrls: [
    'https://telemetry.example.com/',
    /^https:\/\/metadata\.google\.internal\//,
    (url) => url.hostname.endsWith('.internal.example.com'),
  ],
};

Recording and replaying

Create or update a cassette:

npx spectest --recording=record

Replay from the cassette:

npx spectest --recording=replay

Handling replay misses

missingRecordingBehavior controls what happens when replay can’t find a matching cassette entry:

ValueBehavior
failFail the outbound request with a clear unmatched-recording error
recordAllow the real outbound request and save it to the cassette
bypassAllow the real outbound request without saving it

recordingExcludeUrls always bypasses cassette handling entirely, regardless of mode. String entries are canonical URL prefix matches, RegExp entries are tested against the canonical URL string, and function entries receive (url, request) and return a boolean.

Per-test overrides

A test case’s recording field overrides only the mode for that test; when unset it inherits the run-level recording config.

export default [
  {
    name: 'Health check bypasses cassette',
    endpoint: '/health',
    recording: 'off',
  },
];

The recording(tests, mode) helper applies this to an entire suite at once.

Using it outside Spectest suites

The framework-agnostic useHttpRecordings helper lets you drive the same cassette from plain Node test runners (Jest, Vitest, node --test), not just .spectest.* suites:

import { useHttpRecordings } from 'spectest/recordings';

let recordings;

beforeAll(async () => {
  recordings = await useHttpRecordings({
    file: '.spectest/cassette.json',
    mode: 'replay',
    missingRecordingBehavior: 'fail',
    recordingExcludeUrls: ['https://telemetry.example.com/'],
  });
});

afterAll(async () => {
  await recordings.dispose();
});

Related: Jest and Vitest for running Spectest alongside those frameworks, and Outbound Throttling for rate-limiting the same outbound calls instead of recording them.