Socket VCR Test

2.0.1 · active · verified Tue Apr 21

socket-vcr-test is a TypeScript-first library (current version 2.0.1) for recording and replaying HTTP interactions in your test suite, ensuring fast, deterministic, and accurate tests. It functions as a fork of `epignosisx/vcr-test`, aiming to provide enhanced features or maintenance. The library offers flexible recording modes (e.g., `once`, `none`, `update`, `all`) to control cassette generation and playback behavior. Key differentiators include built-in support for request masking to handle sensitive data, and a highly extensible request matching system that allows custom logic for comparing URLs, headers, and bodies. It primarily targets modern JavaScript and TypeScript environments for development testing workflows. It's a useful tool for isolating tests from external network dependencies and ensuring consistent test results without relying on live APIs.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to initialize `socket-vcr-test` with `FileStorage`, use `useCassette` to wrap an asynchronous API call, and verifies that the HTTP interactions are recorded and then replayed. It shows both recording (first run) and replaying (subsequent runs) behavior, as well as an example of `RecordMode.update`.

import { join } from 'node:path';
import { VCR, FileStorage, RecordMode } from 'socket-vcr-test';
import { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from 'node:fs';

// Mock an external API call for demonstration purposes
const mockApi = {
  myAwesomeApiCall: async () => {
    console.log('    Making a simulated API call...');
    return new Promise(resolve => setTimeout(() => resolve({ data: 'hello from real API', timestamp: Date.now() }), 50));
  }
};

// Basic mocks for a runnable 'test' context
const expect = (value: any) => ({
  toBeDefined: () => {
    if (value === undefined || value === null) throw new Error('Expected value to be defined');
  },
  toEqual: (expected: any) => {
    if (JSON.stringify(value) !== JSON.stringify(expected)) throw new Error(`Expected ${JSON.stringify(value)} to equal ${JSON.stringify(expected)}`);
  }
});
const describe = (name: string, fn: () => void) => { console.log(`\n-- Running Suite: ${name} --`); fn(); };
const it = (name: string, fn: () => Promise<void>) => {
  console.log(`  Test: ${name}`);
  fn().then(() => console.log(`  ✓ Passed: ${name}`)).catch(e => console.error(`  ✗ Failed: ${name}\n`, e));
};

// Setup a dummy directory for cassettes
const CASSETTES_DIR = join(process.cwd(), '__quickstart_cassettes__');
if (!existsSync(CASSETTES_DIR)) {
  mkdirSync(CASSETTES_DIR, { recursive: true });
} else {
  // Clean up previous run's cassettes for a fresh start
  rmSync(CASSETTES_DIR, { recursive: true, force: true });
  mkdirSync(CASSETTES_DIR, { recursive: true });
}

describe('socket-vcr-test quickstart', () => {
  it('should record and replay an API call using VCR', async () => {
    const cassetteName = 'quickstart_api_call';
    const cassettePath = join(CASSETTES_DIR, cassetteName + '.yml');

    // 1. Initial run: Configure VCR to record (default mode is 'once')
    let vcr = new VCR(new FileStorage(CASSETTES_DIR));
    vcr.mode = RecordMode.once; // Explicitly set, though it's the default
    console.log(`    First run (Recording mode: ${vcr.mode}): Cassette ${cassetteName}.yml should be created.`);

    await vcr.useCassette(cassetteName, async () => {
      const result = await mockApi.myAwesomeApiCall();
      expect(result).toBeDefined();
      expect((result as any).data).toEqual('hello from real API');
    });
    expect(existsSync(cassettePath)).toEqual(true);
    console.log(`    Cassette '${cassetteName}.yml' now exists.`);

    // 2. Second run: VCR should replay from the cassette
    // Re-initialize VCR to clear any internal state from the previous run
    vcr = new VCR(new FileStorage(CASSETTES_DIR));
    vcr.mode = RecordMode.once; // Still 'once', but cassette exists
    console.log(`\n    Second run (Replay mode: ${vcr.mode}): Cassette ${cassetteName}.yml should be replayed.`);
    
    const startTime = Date.now();
    await vcr.useCassette(cassetteName, async () => {
      const result = await mockApi.myAwesomeApiCall();
      expect(result).toBeDefined();
      expect((result as any).data).toEqual('hello from real API');
    });
    const endTime = Date.now();
    console.log(`    API call completed in ${endTime - startTime}ms (expected very fast replay).`);
    
    // Demonstrating `update` mode
    vcr = new VCR(new FileStorage(CASSETTES_DIR));
    vcr.mode = RecordMode.update;
    console.log(`\n    Third run (Update mode: ${vcr.mode}): Simulating change to trigger re-recording if needed.`);
    await vcr.useCassette(cassetteName, async () => {
      // Even if mockApi changes, 'update' would re-record or update existing entries
      const result = await mockApi.myAwesomeApiCall();
      expect(result).toBeDefined();
    });
  });
});

// To run this example: save as `quickstart.ts`, compile with `tsc quickstart.ts`, then `node quickstart.js`

view raw JSON →