zimicjs / zimic

TypeScript-first HTTP request mocking
https://npmjs.com/package/zimic
MIT License
9 stars 2 forks source link
api http mock openapi service-worker tests typegen types typescript

Zimic

TypeScript-first HTTP request mocking

npm   •   Docs   •   Examples   •   Issues   •   Roadmap

[![CI](https://github.com/zimicjs/zimic/actions/workflows/ci.yaml/badge.svg?branch=canary)](https://github.com/zimicjs/zimic/actions/workflows/ci.yaml)  [![Coverage](https://img.shields.io/badge/Coverage-100%25-31C654?labelColor=353C43)](https://github.com/zimicjs/zimic/actions)  [![License](https://img.shields.io/github/license/zimicjs/zimic?color=0E69BE&label=License&labelColor=353C43)](https://github.com/zimicjs/zimic/blob/canary/LICENSE.md) [![NPM Downloads](https://img.shields.io/npm/dm/zimic?style=flat&logo=npm&color=0E69BE&label=Downloads&labelColor=353C43)](https://www.npmjs.com/package/zimic)  [![Stars](https://img.shields.io/github/stars/zimicjs/zimic)](https://github.com/zimicjs/zimic) 

Zimic is a lightweight, thoroughly tested, TypeScript-first HTTP request mocking library, inspired by Zod's type inference and using MSW under the hood.

Features

Zimic provides a flexible and type-safe way to mock HTTP requests.

import { type HttpSchema } from 'zimic/http';
import { httpInterceptor } from 'zimic/interceptor/http';

// 1. Declare your types
interface User {
  username: string;
}

interface RequestError {
  code: string;
  message: string;
}

// 2. Declare your HTTP schema
// https://bit.ly/zimic-interceptor-http-schemas
type MySchema = HttpSchema<{
  '/users': {
    POST: {
      request: { body: User };
      response: {
        201: { body: User };
        400: { body: RequestError };
        409: { body: RequestError };
      };
    };

    GET: {
      request: {
        headers: { authorization: string };
        searchParams: {
          username?: string;
          limit?: `${number}`;
        };
      };
      response: {
        200: { body: User[] };
        400: { body: RequestError };
        401: { body: RequestError };
      };
    };
  };

  '/users/:userId': {
    PATCH: {
      request: {
        headers: { authorization: string };
        body: Partial<User>;
      };
      response: {
        204: {};
        400: { body: RequestError };
      };
    };
  };
}>;

// 3. Create your interceptor
// https://bit.ly/zimic-interceptor-http#httpinterceptorcreateoptions
const myInterceptor = httpInterceptor.create<MySchema>({
  type: 'local',
  baseURL: 'http://localhost:3000',
  saveRequests: true, // Allow access to `handler.requests()`
});

// 4. Manage your interceptor lifecycle
// https://bit.ly/zimic-guides-testing
beforeAll(async () => {
  // 4.1. Start intercepting requests
  // https://bit.ly/zimic-interceptor-http#http-interceptorstart
  await myInterceptor.start();
});

afterEach(() => {
  // 4.2. Clear interceptors so that no tests affect each other
  // https://bit.ly/zimic-interceptor-http#http-interceptorclear
  myInterceptor.clear();
});

afterAll(async () => {
  // 4.3. Stop intercepting requests
  // https://bit.ly/zimic-interceptor-http#http-interceptorstop
  await myInterceptor.stop();
});

// Enjoy mocking!
test('example', async () => {
  const users: User[] = [{ username: 'diego-aquino' }];

  // 7. Declare your mocks
  // https://bit.ly/zimic-interceptor-http#http-interceptormethodpath
  const myHandler = myInterceptor
    .get('/users')
    // 7.1. Use restrictions to make declarative assertions and narrow down your mocks
    // https://bit.ly/zimic-interceptor-http#http-handlerwithrestriction
    .with({
      headers: { authorization: 'Bearer my-token' },
      searchParams: { username: 'diego' },
    })
    // 7.2. Respond with your mock data
    // https://bit.ly/zimic-interceptor-http#http-handlerresponddeclaration
    .respond({
      status: 200,
      body: users,
    });

  // 8. Run your application and make requests
  // ...

  // 9. Check the requests you expect
  // https://bit.ly/zimic-interceptor-http#http-handlerrequests
  const requests = myHandler.requests();
  expect(requests).toHaveLength(1);

  // The following expects are automatically checked by the restrictions
  // we declared above. Requests not matching them will cause warnings and not
  // be intercepted.

  // If you are not using restrictions, asserting the requests manually is
  // a good practice:
  expect(requests[0].headers.get('authorization')).toBe('Bearer my-token');

  expect(requests[0].searchParams.size).toBe(1);
  expect(requests[0].searchParams.get('username')).toBe('diego');

  expect(requests[0].body).toBe(null);
});

Documentation

[!NOTE]

Zimic has gone a long way in v0, but we're not yet v1!

Reviews and improvements to the public API are possible, so breaking changes may exceptionally land without a major release during v0. Despite of that, we do not expect big mental model shifts. Usually, migrating to a new Zimic release requires minimal to no refactoring. During v0, we will follow these guidelines:

  • Breaking changes, if any, will be delivered in the next minor version.
  • Breaking changes, if any, will be documented in the version release, along with a migration guide detailing the introduced changes and suggesting steps to migrate.

From v0.8 onwards, we expect Zimic's public API to become more stable. If you'd like to share any feedback, please feel free to open an issue or create a discussion!

Examples

Visit our examples to see how to use Zimic with popular frameworks, libraries, and use cases!

Changelog

The changelog is available on our GitHub Releases page.

Contributing

Interested in contributing to Zimic? Check out our contributing guide to get started!