React Testing Library点击按钮触发异步请求测试失败问题
问题根因
你的测试跑不通核心是3个问题:
- 没有Mock后端请求:测试环境是jsdom,不会真的发起服务端请求,点击过滤按钮后请求一直处于pending状态,拿不到返回值就不会触发
setFiltroFormData更新,filtroFormData一直是空对象,满足不了Object.keys(filtroFormData).length > 0的渲染条件,列表自然出不来。 - 交互写法有问题:你手动用
act()包裹同步的fireEvent操作本身就是多余的(RTL的事件方法默认已经做了act包裹),而且fireEvent.input不会触发受控表单组件的onChange监听,输入框的值根本没有同步到组件状态里;另外你把输入、点击操作都放在同步act块里,没有等待交互触发的状态更新、异步请求的微任务执行。 - 等待逻辑不完整:
waitFor会一直轮询直到断言通过或者超时,但前提是触发状态更新的异步流程能正常走完,请求都没mock的话等到超时也等不到列表渲染。
修复步骤
1. 先Mock接口请求
把组件里调用的请求方法Mock掉,返回符合业务逻辑的假数据,保证点击过滤后能正常走完请求、更新状态的流程。比如你的请求是从独立服务模块引入的,直接在测试文件顶部Mock:
// 替换成你实际的请求模块路径和方法名 jest.mock('../services/processo', () => ({ fetchProcessos: jest.fn(() => Promise.resolve({ // 返回结构和你真实接口保持一致,保证setFiltroFormData拿到的是非空值 data: { list: [], total: 0 } })) }))
2. 用官方推荐的userEvent代替裸fireEvent
userEvent会模拟真实用户的完整交互路径(比如输入会触发focus、keydown、input、change等一系列事件),比只触发单个事件的fireEvent更贴近真实场景,注意所有userEvent操作都是异步的,需要加await,不要手动包act()。
3. 调整等待逻辑
用自带异步等待能力的findBy系列查询代替waitFor包裹getBy,代码更简洁,也能正确捕获异步状态更新。
修复后的测试代码
import userEvent from '@testing-library/user-event'; // 这里放刚才写的接口Mock代码 it('Deve renderizar o processo lista', async () => { const user = userEvent.setup(); const { getByRole, findByText } = render( <HelmetProvider> <MemoryRouter> <ProcessoGerenciamento /> </MemoryRouter> </HelmetProvider>, ); // 输入查询条件 await user.type(getByRole('textbox', { name: /número do processo/i }), '1'); // 点击过滤按钮 await user.click(getByRole('button', { name: /filtrar/i })); // 等待列表标题渲染 const processoTitulo = await findByText('Processos'); expect(processoTitulo).toBeInTheDocument(); });
额外排查点
- 如果Mock后还是不通过,检查Mock返回的数据结构是不是和你
filtrarProcesso方法里的解析逻辑匹配,比如有没有判断接口返回code、有没有取错返回字段,必须保证setFiltroFormData最终传入的是非空对象,否则列表不会渲染。 - 确认
getByRole('textbox', { name: /número do processo/i })拿到的是正确的输入元素:如果是自定义输入组件,要检查label有没有和input正确关联,否则输入操作会打到错误元素上,表单状态里的查询参数一直是空的。 - 不要在测试里手动写
act()包裹RTL的方法,RTL本身已经处理了act的兼容,手动包裹反而容易因为漏等异步任务导致状态更新不被捕获。
内容的提问来源于stack exchange,提问作者Renan Morais
相关产品推荐
相关产品推荐

