常见问题

有关 Mock Service Worker 的常见问题。

它与 XYZ 库有何不同?

¥How is it different than library XYZ?

请参阅 比较页面 以了解 Mock Service Worker 和其他流行 API 模拟库之间的详细技术和概念比较。

¥Please see the Comparison page for detailed technical and conceptual comparison between Mock Service Worker and other popular API mocking libraries.

简而言之,大多数解决方案在应用级别提供请求拦截,而 Mock Service Worker 在网络级别拦截请求。它还允许你不仅将相同的模拟定义用于测试,还用于开发和调试,无需配置、适配器或插件即可跨不同工具进行集成。

¥In a nutshell, most solutions provide requests interception on the application level, while Mock Service Worker intercepts requests on the network level. It also allows you to use the same mock definition not only for testing, but for development and debugging as well, integrating across different tools without configurations, adapters, or plugins.

它是否支持请求库 XYZ?

¥Does it support request library XYZ?

是的。Mock Service Worker 支持所有请求库,包括现有的和即将在未来发布的库。这是通过在网络级别模拟 API 获得的好处之一。

¥Yes. Mock Service Worker supports all request libraries, both existing and those about to be released in the future. This is one of the benefits you get by mocking your API at the network level.

我可以在 Node.js 中使用它吗?

¥Can I use it in Node.js?

是的。尽管 Node.js 中没有 Service Worker,但 MSW 为你提供了指定的 API,以便在 Node.js 中重用相同的请求处理程序。按照下面的集成来学习如何在 Node.js 中使用 MSW:

¥Yes. Although there’s no Service Worker in Node.js, MSW provides you with a designated API to reuse the same request handlers in Node.js. Follow the integration below to learn how to use MSW in Node.js:

Node.js integration

Learn how to integrate Mock Service Worker in any Node.js process.

我可以在 React Native 中使用它吗?

¥Can I use it in React Native?

是的,你可以在开发和测试 React Native 应用时使用 MSW。设置与 Node.js 中的设置类似,你可以按照本指南了解更多信息:

¥Yes, you can use MSW while developing and testing your React Native application. The setup would be similar to that in Node.js, and you can learn more about it following this guide:

Node.js integration

Learn how to integrate Mock Service Worker in a Node.js process.

参考错误:fetch 未在 Node.js 中定义

¥ReferenceError: fetch is not defined in Node.js

此错误意味着你使用的 Node.js 版本不支持全局 Fetch API。

¥This error means that the version of Node.js you’re using doesn’t support the global Fetch API.

通过升级到 Node.js 版本 18 或更高版本解决此问题。MSW 不支持低于版本 18 的 Node.js 版本。

¥Resolve this by upgrading to Node.js version 18 or higher. MSW does not support Node.js versions below version 18.

Request/Response/TextEncoder 未定义(Jest)

¥Request/Response/TextEncoder is not defined (Jest)

此问题是由于你的环境由于某种原因没有 Node.js 全局变量而导致的。这通常发生在 Jest 中,因为它故意剥夺了你的 Node.js 全局变量,并且无法完全重新添加它们。因此,你必须自己明确添加它们。

¥This issue is caused by your environment not having the Node.js globals for one reason or another. This commonly happens in Jest because it intentionally robs you of Node.js globals and fails to re-add them in their entirely. As the result, you have to explicitly add them yourself.

在你的 jest.config.js 旁边创建一个 jest.polyfills.js 文件,内容如下:

¥Create a jest.polyfills.js file next to your jest.config.js with the following content:

// jest.polyfills.js
/**
 
 * @note The block below contains polyfills for Node.js globals
 
 * required for Jest to function when running JSDOM tests.
 
 * These HAVE to be require's and HAVE to be in this exact
 
 * order, since "undici" depends on the "TextEncoder" global API.
 
 *  * Consider migrating to a more modern test runner if
 
 * you don't want to deal with this.
 */
 
const { TextDecoder, TextEncoder } = require('node:util')
 
Object.defineProperties(globalThis, {
  TextDecoder: { value: TextDecoder },
  TextEncoder: { value: TextEncoder },
})
 
const { Blob, File } = require('node:buffer')
const { fetch, Headers, FormData, Request, Response } = require('undici')
 
Object.defineProperties(globalThis, {
  fetch: { value: fetch, writable: true },
  Blob: { value: Blob },
  File: { value: File },
  Headers: { value: Headers },
  FormData: { value: FormData },
  Request: { value: Request },
  Response: { value: Response },
})

确保安装 undici。它是 Node.js 中的官方获取实现。

¥Make sure to install undici. It’s the official fetch implementation in Node.js.

然后,将 jest.config.js 中的 setupFiles 选项设置为指向新创建的 jest.polyfills.js:

¥Then, set the setupFiles option in jest.config.js to point to the newly created jest.polyfills.js:

// jest.config.js
module.exports = {
  setupFiles: ['./jest.polyfills.js'],
}

如果你发现此设置很麻烦,请考虑迁移到现代测试框架,例如 Vitest,它没有任何 Node.js 全局变量问题并提供开箱即用的原生 ESM 支持。

¥If you find this setup cumbersome, consider migrating to a modern testing framework, like Vitest, which has none of the Node.js globals issues and provides native ESM support out of the box.

为什么我应该从请求处理程序 URL 中删除查询参数?

¥Why should I drop query parameters from the request handler URL?

查询参数不描述 RESTful 资源。相反,它们向服务器提供额外的数据。在请求匹配期间,MSW 将自动剥离查询参数,并且不会产生任何影响。

¥Query parameters do not describe RESTful resources. Instead, they provide additional data to the server. Query parameters will be automatically stripped by MSW during the request matching and will have no effect.

将请求处理程序视为服务器端路由处理程序更容易理解这一点:在服务器上路由时不包括查询参数,因此在使用 MSW 路由时也不应该包括查询参数。

¥It’s easier to understand this by thinking of request handlers as server-side route handlers: you do not include query parameters when routing on the server, so neither should you when routing with MSW.

请注意,你可以使用 URL API 访问请求处理程序中的查询参数:

¥Note that you can access query parameters in the request handler by using the URL API:

// Describe the resource path: "/post".
http.get('/post', ({ request }) => {
  // Convert the request URL string to a URL instance
  // so the browser would parse query parameters for you.
  const url = new URL(request.url)
 
  // Access the query parameters from the URL instance.
  // For example: GET /post/id=abc-123 → id: "abc-123"
  const id = url.searchParams.get('id')
 
  return HttpResponse.json({
    id,
    title: 'The Empowering Limitation',
  })
})

为什么我会得到过时的响应 react-query/SWR/Apollo/等?

¥Why do I get stale responses with react-query/SWR/Apollo/etc.?

某些请求客户端的缓存机制可能会在你的测试中产生旧的响应。请确保在每个测试套件之前/之后清除缓存,以使你的测试保持可预测性。

¥Caching mechanism of some request clients may produce stale responses in your tests. Make sure you clear the cache before/after each test suite for your tests to remain predictable.

react-query

import { QueryCache } from 'react-query'
 
const queryCache = new QueryCache()
 
afterEach(() => {
  queryCache.clear()
})

SWR

import { cache } from 'swr'
 
beforeEach(() => {
  cache.clear()
})

Apollo 客户端

¥Apollo Client

Apollo Client 团队建议为每个测试创建一个新的客户端实例。

¥The Apollo Client team recommends creating a new client instance for each test.

// src/apollo-client.js
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'
 
const httpLink = new HttpLink({
  uri: 'https://example.com/graphql',
})
 
export function makeClient() {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link: httpLink,
  })
}
// test/Component.test.jsx
import { makeClient } from '../src/apollo-client'
 
it('renders the component', async () => {
  const client = makeClient()
  // ...use your client in test.
})

Apollo Client 文档 中了解更多信息。

¥Learn more in the Apollo Client documentation.

何时使用浅色主题?

¥Light theme when?

只要你有时间 打开拉取请求

¥Whenever you have time to open a pull request.

MSW 在并发测试中不起作用

¥MSW doesn’t work in concurrent tests

如果你的测试套件具有多个修改网络行为的并发测试(即有 .use() 调用),则必须使用 server.boundary() API 将网络范围限定到每个测试。

¥If your test suite features multiple concurrent test that modify the network behavior (i.e. have .use() calls), you must scope the network to each test by using the server.boundary() API.

server.boundary()

Scope the network interception to the given boundary.