如何在React Testing Library中用MSW实现基于请求的动态响应?
如何让Mock Service Worker根据请求动态返回响应?
问题场景
我正在测试一个表单,流程如下:
- 表单初始通过GET请求加载数据并渲染UI,此时
protocol为http,HTTPS单选框未勾选 - 勾选HTTPS单选框后点击保存,触发PUT请求提交更新后的
protocol值 - 提交完成后,UI会再次发起GET请求刷新数据,我需要此时返回的
protocol是PUT提交的https,并验证UI是否正确渲染
当前的MSW mock只能固定返回初始的http,无法根据PUT请求的内容动态更新后续GET响应,请问怎么实现?
解决方案
核心是在测试作用域内维护一个共享状态变量,让PUT请求的mock更新这个变量,GET请求的mock读取这个变量返回响应。修改后的代码如下:
it('should update form and reflect changes on UI', async () => { // 定义共享状态,存储当前的protocol值 let currentProtocol = 'http'; server.use( rest.get(`*/request-to-get-data-and-show on UI`, (_req, res, ctx) => { // 每次GET请求都返回当前的protocol值,不要用res.once() return res( ctx.delay(2000), ctx.status(200), ctx.json({ detail: 'ok', protocol: currentProtocol, // 使用共享状态的值 ip: 'fvackq.uksouth.cloudapp.azure.com', port: 80, }) ); }), rest.put(`*/request-to-update-db`, (_req, res, ctx) => { const data = _req.body as { ip: string; port: number; protocol: string; }; expect(data.protocol).toEqual('https'); // 更新共享状态为PUT提交的protocol值 currentProtocol = data.protocol; return res(ctx.delay(2000), ctx.status(200)); }) ); render(<Settings />); const button1 = await screen.findByRole('radio', { name: 'Https', checked: false }); fireEvent.click(button1, { target: { checked: true } }); await screen.findByRole('radio', { name: 'Https', checked: true }); const saveButton = screen.getByRole('button', { name: /save/i }); fireEvent.click(saveButton); // 验证第二次GET请求后UI是否正确渲染了https状态 await screen.findByRole('radio', { name: 'Https', checked: true }); }, 10000);
关键说明
- 共享状态的作用域:必须把
currentProtocol定义在server.use的外面,确保GET和PUT的mock都能访问并修改它 - 移除res.once():原来的
res.once()只会响应一次GET请求,后续的请求会失败,所以要去掉,让每次GET请求都能返回最新状态 - 状态更新时机:在PUT请求的mock中,验证完请求体后立即更新共享状态,这样后续的GET请求就能拿到最新值
- 测试隔离:如果有多组测试,记得在
beforeEach中重置currentProtocol的值,避免测试之间的状态污染
内容的提问来源于stack exchange,提问作者hafiz ali
相关产品推荐
相关产品推荐

