npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@matthieug/shm

v0.7.1

Published

Simple http mocking for unit tests in node, and react-native, with good developer experience

Downloads

1,387

Readme


yarn add --dev @matthieug/shm

| Platform | Status | Notes | | ------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------- | | node / jest | ✅ | node>=18 required | | node / jest / jsdom | ✅ | Polyfills required | | node / vitest | ✅ | node>=18 required | | bun with bun test | ⚠️ | test won't fail with afterEach(expectRequestsToMatchHandlers) | | expo | ✅ | Install react-native-url-polyfill if using SDK < 50 | | react-native | ✅ | Install react-native-url-polyfill | | browser | ✅ | |

Basic usage

// Some setup/global file
import { installInterceptor } from "@matthieug/shm";

installInterceptor();

// `mockServer.ts` or equivalent
import { createMockServer } from "@matthieug/shm";

export const mockServer = createMockServer("https://test.com");

// Mock a request -- short syntax for the 90% case
mockServer.get<BodyType>("some-route", body);

// Or full syntax for more control
mockServer.get<BodyType>("item/:id", {
  request: { // a request must contain **at least** the specified items to match
    pathParams: { id: "12" } // match path params
    searchParams: { lang: "fr" } // match search params
    headers: { Authorization: "Bearer some-token" } // match headers
  },
  response: {
    body: { message: "here is your mock" }, // specify a json or string body
    status: 418 // specify a status code, default is 200
  }
});

// All usual http methods are available

Have a look at the type definitions for more details.

Important notes:

  • Handlers will by default only respond to ONE matching request. After that, they will be "consumed"
  • Handlers are used in a first_in_first_out order

Usage in tests

Setup

// `jest-setupAfterEnv.js` or equivalent
import { installInterceptor, expectRequestsToMatchHandlers } from "@matthieug/shm";

// Prevent all outgoing requests -- Unhandled requests will be responded to with a 404
installInterceptor();

// Fail tests when there are unhandled requests or unused handlers, and clear handlers
afterEach(expectRequestsToMatchHandlers);
// `mockServer.ts` or equivalent
import { createMockServer } from "@matthieug/shm";

export const mockServer = createMockServer("https://test.com");

Ensure good DX with expectRequestsToMatchHandlers

Using it in your tests will:

  • keep tests isolated, by resetting the mock handlers
  • enforce removal of the unused handlers that could creep up as your code evolves, by throwing an error if a handler was not called
  • ensure your tests do not pass "by coincidence" and help with debugging issues, by throwing an error if a request was not handled
import { expectRequestsToMatchHandlers } from "@matthieug/shm";

afterEach(expectRequestsToMatchHandlers);

test("some test", async () => {
  mockServer.get("hello", body);
  await fetch("https://test.com/hallo");
});

// SHM: Received requests did not match defined handlers
//   UNHANDLED REQUEST: GET https://test.com/hello
//       --> handler GET https://test.com/hallo -> url /hallo !== /hello

Check that an API call was made with mockHandler.wasCalled

expect(mockHandler.wasCalled()).toBe(true);

This can be useful even if you're using the recommended setup with expectRequestsToMatchHandlers, eg to:

  • make the expectation explicit ("when user clicks this, my app should save that to the backend")
  • wait for the call to have been made
  • check that the call was made at the right time in a multi-step tests

Check that the correct request body was sent with mockHandler.getSentBody

test("my test", async () => {
  const mockHandler = mockServer.post("item", "here is your mock");

  await fetch("https://test.com/item", { method: "POST", body: "here's my request" });

  expect(await mockHandler.getSentBody()).toEqual("here's my request");
});

What's different from other http mocking libraries?

There are great alternatives out there, like msw or nock. By the way this package is using @mswjs/interceptors under the hood in node and the browser.

We want to promote a certain way to define and use api mocks in tests, and provide a very simple API.

  • Enforce maintenance of API mocks, by failing tests on unhandled requests and unused handlers
  • No way to write complex request matchers. Tests should avoid conditionnals, and this principle includes your mock definitions (otherwise you should write tests for your tests 🤔)
  • Check that your code is sending the correct request through assertions, instead of by coincidentally definining the right handler
  • Prefer specifying the necessary mocks for each test, so that you know at a glance what APIs your feature/component needs

Usage in an app

All the basic APIs are available, but there are a few specific options that you may want to use in this case:

import {
  installInterceptor,
  createMockServer,
  uninstallInterceptor,
  passthrough,
} from "@matthieug/shm";

// Start intercepting -- let unhandled requests passthrough
installInterceptor({ onUnhandled: passthrough });

const mockServer = createMockServer("https://test.com", {
  // options specified here will apply to all handlers
  delayMs: 500, // view your loading states
  persistent: true, // allow handlers to respond to multiple matching requests
});

// When you want to make real requests again
uninstallInterceptor();

Future plans

I don't plan to add lots of features, but I strive for very high quality. Don't hesitate to open an issue if you find a bug, or if the docs are unclear, or if an error message is not helpful.