Mock Service Worker (MSW)

repository·main·Indexed 12 days ago

https://github.com/mswjs/msw

An industry-standard API mocking library for JavaScript that intercepts requests at the network level. MSW allows developers to mock REST, GraphQL, and WebSocket APIs across both browser and Node.js environments. In the browser, it utilizes the Service Worker API, while in Node.js it uses a low-level interception algorithm, ensuring application code remains unaware of the mocking.

Tokens
9K
Snippets
34
Records
57
Agent score
97%

What's inside MSW

  1. How MSW intercepts requests

    main

    MSW intercepts requests at the network level rather than stubbing high-level APIs like fetch or axios.

    • In the Browser: It uses the Service Worker API to intercept requests after they have left your application code. This allows you to see mocked responses directly in the browser's 'Network' tab.
    • In Node.js: It implements a low-level interception algorithm that allows the same request handlers used in the browser to work in Node.js environments.

    Because interception happens at the network level, your application code remains 'deviation-free'—it runs exactly as it would in production, unaware that the responses are being provided by MSW.

  2. Understand MSW's TypeScript peer dependency policy

    main

    MSW does not enforce an upper version limit on the typescript peer dependency. This allows users to migrate to newer versions of TypeScript without waiting for an MSW update.

    Because TypeScript does not follow standard SemVer (breaking changes can occur in minor versions), MSW uses automated nightly builds to validate the library against the latest TypeScript nightly releases. This ensures compatibility and early detection of issues caused by TypeScript's versioning behavior.

  3. Understand MSW support for Jest

    main

    MSW does not offer official support for Jest or JSDOM. While MSW can be used with Jest, the MSW maintainers do not address issues specific to Jest or JSDOM (such as ESM support limitations or inconsistencies between the browser and JSDOM).

    If you encounter issues, they are likely caused by Jest or JSDOM's implementation rather than MSW itself. You should report such issues to the respective Jest or JSDOM repositories.

  4. Quick start: Mocking in the browser

    main

    To use MSW in a browser environment, you use the setupWorker API from msw/browser. This utilizes the Service Worker API to intercept requests at the network level, meaning your application code remains unaware of the mocking.

    1. Import http, HttpResponse from msw and setupWorker from msw/browser.
    2. Define request handlers using http methods (e.g., http.get).
    3. Initialize the worker with setupWorker.
    4. Start the worker using await worker.start().
    // 1. Import the library.
    import { http, HttpResponse } from 'msw'
    import { setupWorker } from 'msw/browser'
    
    // 2. Describe network behavior with request handlers.
    const worker = setupWorker(
      http.get('https://github.com/octocat', ({ request, params, cookies }) => {
        return HttpResponse.json(
          {
            message: 'Mocked response',
          },
          {
            status: 202,
            statusText: 'Mocked status',
          },
        )
      }),
    )
    
    // 3. Start mocking by starting the Service Worker.
    await worker.start()
  5. Quick start: Mocking in Node.js

    main

    In Node.js environments where Service Workers are unavailable, MSW uses a low-level interception algorithm to intercept requests. This allows you to use the same request handlers in Node.js as you do in the browser, providing a single source of truth for network behavior.

    To use MSW in Node.js:

    1. Import http, HttpResponse from msw and setupServer from msw/node.
    2. Initialize the server with setupServer().
    3. Use server.use() within your application logic or tests to define specific request behaviors.
    4. You can also use server.boundary() to scope request interception to a specific closure.
    import express from 'express'
    import { http, HttpResponse } from 'msw'
    import { setupServer } from 'msw/node'
    
    const app = express()
    const server = setupServer()
    
    app.get(
      '/checkout/session',
      server.boundary((req, res) => {
        // Describe the network for this Express route.
        server.use(
          http.get(
            'https://api.stripe.com/v1/checkout/sessions/:id',
            ({ params }) => {
              return HttpResponse.json({
                id: params.id,
                mode: 'payment',
                status: 'open',
              })
            },
          ),
        )
    
        // Continue with processing the checkout session.
        handleSession(req, res)
      }),
    )
  6. How WebSocket interception works with `ws.link()`

    main

    MSW intercepts WebSocket connections by matching the outgoing request URL against the path provided to ws.link(url).

    When a match occurs, MSW intercepts the HTTP upgrade request. If you have registered an event listener via addEventListener on the link, MSW will trigger that listener (e.g., the connection event) and provide a client object that allows you to simulate server-side communication using .send().

  7. Configure an active network instance

    main

    The configure() method on the NetworkApi allows you to override initial settings provided during defineNetwork. However, this method is only available while the network is in the NetworkReadyState.DISABLED state. If you attempt to call configure() on an enabled network, it will throw an error.

    // This is valid
    const network = defineNetwork({ sources: [...] });
    network.configure({ onUnhandledFrame: 'error' });
    await network.enable();
    
    // This will throw an error
    await network.enable();
    network.configure({ onUnhandledFrame: 'error' });
  8. Troubleshoot MSW issues in Jest

    main

    If you are experiencing issues using MSW within a Jest environment, follow these steps to determine if the issue is caused by MSW:

    1. Verify outside of Jest: Run your code in a plain Node.js script to see if the issue persists.
    2. Try Vitest: Copy your problematic test to Vitest to see if the issue is related to Jest's environment or ESM handling.
    3. Use official examples: Use the MSW Usage examples repository as a template to create a minimal reproduction of your issue.

    Note: MSW does not support issue reports from non-standard environments like Deno or Bun.

  9. Resolve linting errors from mockServiceWorker.js

    main

    If your linting or code formatting tools (like ESLint or Prettier) report warnings or errors originating from the mockServiceWorker.js file, do not attempt to modify the script to add ignore pragma comments.

    Instead, configure your linting tools to ignore the worker script. The mockServiceWorker.js is a static asset, not application code, and should be treated like any other file in your /public directory.

    Recommended Solutions:

    1. Ignore the specific file: Add mockServiceWorker.js to your linting/formatting ignore configuration.
    2. Ignore the public directory: If your project convention allows, add your entire public directory (e.g., /public) to your linting and prettifying ignore lists.
  10. Coerce MSW paths for path-to-regexp compatibility

    main

    The coercePath function transforms MSW-style path strings into a format compatible with the path-to-regexp library. It performs the following transformations:

    1. Wildcard Conversion: Replaces wildcards (*) with unnamed capturing groups (.*) while preserving parameter modifiers (e.g., :name*).
    2. Port Escaping: Escapes the colon in ports (e.g., :8080) so they can be matched in absolute URLs.
    3. Protocol Escaping: Escapes the colon in protocols (e.g., https:) to allow matching of absolute URLs.
  11. Mock GraphQL queries with graphql.query()

    main

    Use graphql.query() to intercept a specific GraphQL query by its operation name. The resolver function should return an HttpResponse containing the mocked data structure (typically wrapped in a data key).

    graphql.query('GetUser', () => {
      return HttpResponse.json({ data: { user: { name: 'John' } } })
    })
  12. Mock GraphQL mutations with graphql.mutation()

    main

    Use graphql.mutation() to intercept a specific GraphQL mutation by its operation name. The resolver function should return an HttpResponse containing the mocked response data.

    graphql.mutation('SavePost', () => {
      return HttpResponse.json({ data: { post: { id: 'abc-123' } } })
    })