React Testing Library测试提前完成,模态框关闭测试因useEffect定时器失败
解决React Testing Library测试模态框关闭失败的问题
问题核心
你遇到的问题是模态框依赖定时器延迟关闭,而测试中waitForElementToBeRemoved的使用方式有误,导致无法正确等待元素移除。
修正方案
方案1:改用queryByTestId替代getByTestId
getByTestId在找不到元素时会直接抛出错误,导致waitForElementToBeRemoved提前失败。换成queryByTestId(找不到元素返回null),就能让RTL正确监测元素从存在到消失的过程:
test("testname", async () => { const user = userEvent.setup(); const { store } = renderWithProviders(<App />); const editBtn = screen.getByTestId("edit-item"); await user.click(editBtn); const editPopupInput = screen.getByTestId("edit-popup-input"); const savePopupBtn = screen.getByTestId("save-popup-btn"); expect(editPopupInput).toBeInTheDocument(); expect(savePopupBtn).toBeInTheDocument(); await user.type(editPopupInput, 'text'); expect(editPopupInput).toHaveValue('text'); // 直接使用已获取的按钮,无需重复findBy await user.click(savePopupBtn); expect(store.getState().data.items[0].title).toEqual('text'); // 用queryByTestId避免找不到元素时抛出错误 await waitForElementToBeRemoved(() => screen.queryByTestId("save-popup-btn"), { timeout: 1000, }); });
方案2:直接传入已获取的元素
waitForElementToBeRemoved可以直接接收已获取的DOM元素,RTL会自动跟踪它是否从DOM中移除:
// 前面代码与方案1一致 await user.click(savePopupBtn); expect(store.getState().data.items[0].title).toEqual('text'); // 直接传入之前获取的savePopupBtn元素 await waitForElementToBeRemoved(savePopupBtn, { timeout: 1000 });
方案3:模拟定时器(推荐,提升测试速度)
如果模态框关闭是通过setTimeout实现的,用Jest假定时器跳过真实延迟,大幅加快测试:
test("testname", async () => { // 启用假定时器 jest.useFakeTimers(); const user = userEvent.setup(); const { store } = renderWithProviders(<App />); const editBtn = screen.getByTestId("edit-item"); await user.click(editBtn); const editPopupInput = screen.getByTestId("edit-popup-input"); const savePopupBtn = screen.getByTestId("save-popup-btn"); expect(editPopupInput).toBeInTheDocument(); expect(savePopupBtn).toBeInTheDocument(); await user.type(editPopupInput, 'text'); expect(editPopupInput).toHaveValue('text'); await user.click(savePopupBtn); expect(store.getState().data.items[0].title).toEqual('text'); // 快进所有定时器,触发模态框关闭逻辑 jest.runAllTimers(); // 等待React完成DOM更新 await waitFor(() => {}); // 验证按钮已不存在 expect(screen.queryByTestId("save-popup-btn")).not.toBeInTheDocument(); // 恢复真实定时器 jest.useRealTimers(); });
关键说明
getBy系列方法用于确定元素一定存在的场景,找不到会抛出错误;queryBy用于检查元素是否不存在的场景,找不到返回null。- 使用假定时器时,要记得在测试结束后恢复真实定时器,避免影响其他测试。
内容的提问来源于stack exchange,提问作者uhidga
相关产品推荐
相关产品推荐

