使用 Jest 和 OpenAPI mocks 测试 React
Kent C. Dodds 最近写了一篇非常有趣的文章,window.fetch
呼吁在测试 React 应用程序时停止模拟:
他是对的。
我最近刚刚将一个 React 项目从基于 fetch 的 API 客户端实现迁移到基于 axios 的实现,而后者的测试严重依赖于 mocking global.fetch
。很快我就发现这不是一个好的做法。
我最终不得不自己写一个测试工具来模拟 fetch 和新的 API 客户端。当你不得不修改测试来证明你的代码没有给用户带来任何改变时,这看起来总是很糟糕。
作为更好的替代方案,Kent 建议使用Mock Service Worker。更具体地说,msw 模块本质上是一个模拟后端,它作为 Service Worker 运行,拦截所有传出的 API 请求并进行处理。
设置 msw
使用 msw 为你的 React 测试设置一个模拟后端其实相当简单。如果你想更深入地了解,可以看看Kent 的原帖,但以下是在测试代码中模拟 REST 端点真正需要做的事情:
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('/api/pets', (req, res, ctx) => {
const pets = [{ id: 1, name: 'Garfield', type: 'cat' }];
return res(ctx.json({ pets }));
}),
);
beforeAll(() => server.listen());
afterAll(() => server.close());
这非常酷的原因之一是因为它避免了必须启动真实的本地模拟后端的痛苦,例如需要绑定到运行测试的主机上的特定端口的快速服务器。
这有助于让您的测试运行得更快、更简单,正如它们应该的那样。
使用 OpenAPI 效果更佳
作为一个经常使用提供 Swagger/OpenAPI 定义的 API 后端(希望如此!)的人,我已经在 React 测试中使用openapi-backend的 OpenAPI mocks 来模拟后端了。当我了解到 时msw
,我非常激动!
msw
事实证明,这openapi-backend
是模拟 REST API 的完美组合。
为了提供 API 的完整模拟,我所需要做的就是使用 API 定义创建一个带有 openapi-backend 的模拟后端,并告诉 msw 使用它:
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import OpenAPIBackend from 'openapi-backend';
import definition from './path/to/definition.json';
// create our mock backend with openapi-backend
const api = new OpenAPIBackend({ definition });
api.register('notFound', (c, res, ctx) => res(ctx.status(404)));
api.register('notImplemented', async (c, res, ctx) => {
const { status, mock } = api.mockResponseForOperation(c.operation.operationId);
ctx.status(status);
return res(ctx.json(mock));
});
// tell msw to intercept all requests to api/* with our mock
const server = setupServer(
rest.all('/api/*', async (req, res, ctx) => api.handleRequest(
{
path: req.url.pathname,
query: req.url.search,
method: req.method,
body: req.bodyUsed ? await req.json() : null,
headers: { ...req.headers.raw },
},
res,
ctx,
)),
);
beforeAll(() => server.listen());
afterAll(() => server.close());
现在,您不必为每个操作编写自己的模拟处理程序,而是根据 OpenAPI 文档中定义的响应模式和示例生成它们。
更重要的是:每当 API 定义发生变化时,您的所有模拟都会自动更新,让您进一步确信您的应用与新 API 版本兼容。
启用请求验证
测试时,确保您的应用程序确实向 API 发送了正确的请求通常非常有用。
使用 OpenAPI 定义的好处是 API 操作定义明确,并且可以使用 JSON 模式自动验证请求。
要在测试期间启用请求验证,您只需为 openapi-backend注册validationFail 处理程序:
api.register('validationFail', (c, res, ctx) => res(
ctx.status(400),
ctx.json({ error: c.validation.errors }),
));
运行测试时,对 API 端点的错误调用现在将导致模拟后端出现 400 错误请求错误,同时出现一条有用的错误消息,告诉您请求出了什么问题。
自定义处理程序
在某些测试中,提供与 openapi-backend 提供的默认模拟不同的模拟可能是有意义的。
在测试中为 API 操作注册自己的模拟非常简单,只需api.register()
使用 operationId 和模拟处理程序进行调用即可:
it('should call getPets operation', () => {
// given
const mockResponse = [{ id: 2, name: 'Odie' }];
const mockHandler = jest.fn((c, res, ctx) => res(ctx.json(mockResponse)));
api.register('getPets', mockHandler);
// when
// render(<MyComponent />)...
// then
expect(mockHandler).toBeCalled();
});
结论
了解 msw 对我的 React 测试来说意义重大。它与openapi-backend的自动模拟功能相结合,让 React 测试中的 API 模拟变得轻而易举。
感谢 Kent 和mswjs/msw背后的团队!💙
鏂囩珷鏉ユ簮锛�https://dev.to/epilot/testing-react-with-jest-and-openapi-mocks-8gc