HTTP Interaction Recorder for Tests
vcr-test is a testing utility that records and replays HTTP interactions, enabling fast, deterministic, and accurate tests by eliminating reliance on live external APIs. It intercepts HTTP traffic from any client (`fetch`, `axios`, `got`, etc.) using `@mswjs/interceptors` and stores request/response pairs in YAML "cassette" files. The current stable version is 1.4.0. It provides flexible recording modes (once, none, update, all) and extensibility for request masking, pass-through, and custom matching. Its key differentiator is its framework-agnostic nature and support for modern JavaScript features like `await using`, while also offering a callback API for older environments. While no explicit release cadence is stated, the project appears actively maintained on GitHub.
Common errors
-
Error: Cassette 'cassette_name' not found and mode is 'none'.
cause The VCR instance's `mode` is set to `RecordMode.none` (or `VCR_MODE=none`), and the specified cassette file does not exist.fixChange the `vcr.mode` to `RecordMode.once` (default), `RecordMode.update`, or `RecordMode.all` to allow recording the cassette, or ensure the cassette exists before running the test in `none` mode. -
TypeError: Invalid value for 'headers': 'content-length' should be a number.
cause This error can occur if a manually edited cassette has an invalid or non-numeric value for the `content-length` header in a response.fixOpen the YAML cassette file and ensure the `content-length` header value for the affected interaction is an integer representing the byte length of the response body, or delete the cassette and re-record it.
Warnings
- breaking The `await using` syntax for cassette management requires Node.js 22+ and TypeScript 5.2+. Using `vcr-test` with older versions of Node.js or TypeScript will result in syntax errors or runtime issues if this pattern is adopted.
- gotcha When manually editing recorded YAML cassette files, especially modifying response bodies, ensure that the `content-length` header in the `response.headers` section is updated to accurately reflect the new body's length. Mismatched `content-length` can lead to unexpected client behavior or parsing issues.
- gotcha The `VCR_MODE` environment variable takes precedence over any programmatically set `vcr.mode` property. This can lead to unexpected recording behavior if tests are run in an environment where `VCR_MODE` is set (e.g., CI/CD) and overrides local settings.
Install
-
npm install vcr-test -
yarn add vcr-test -
pnpm add vcr-test
Imports
- VCR
const VCR = require('vcr-test').VCRimport { VCR } from 'vcr-test' - FileStorage
const FileStorage = require('vcr-test').FileStorageimport { FileStorage } from 'vcr-test' - RecordMode
const RecordMode = require('vcr-test').RecordModeimport { RecordMode } from 'vcr-test' - DefaultRequestMatcher
import DefaultRequestMatcher from 'vcr-test/DefaultRequestMatcher'
import { DefaultRequestMatcher } from 'vcr-test'
Quickstart
import { join } from 'node:path';
import { VCR, FileStorage } from 'vcr-test';
// This would be your actual API client, e.g., using fetch or axios
const api = {
async myAwesomeApiCall() {
const response = await fetch('https://httpbin.org/post', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'john' })
});
return response.json();
}
};
describe('some suite', () => {
const cassettesPath = join(process.cwd(), '__cassettes__');
const vcr = new VCR(new FileStorage(cassettesPath));
it('should record and replay an API call using await using', async () => {
// Ensure the cassette directory exists and is clean for testing
// For real usage, you'd manage this outside the test.
await using _cassette = await vcr.useCassette('my_test_cassette');
const result = await api.myAwesomeApiCall();
expect(result).toBeDefined();
expect(result.data).toBe('{"name":"john"}');
expect(result.headers['Content-Type']).toContain('application/json');
}, 10000); // Increase timeout for initial recording
});