如何用React Testing Library测试React异步函数?定时器组件测试失败
React组件异步逻辑测试问题修复
问题场景
有一个NewForm组件,通过useEffect结合setTimeout在1秒后更新状态,渲染出文本为"Div"的div元素。尝试用React Testing Library测试该异步逻辑时,测试超时失败。
组件代码
import React, { useEffect, useState } from 'react' const NewForm = () => { const [value, setValue] = useState(null) useEffect(() => { const timer = setTimeout(() => { setValue({name: "Nik"}) }, 1000) return () => { clearTimeout(timer) } }, []) return ( <form> <input type="text" /> <input type="email" /> <button type={'button'}>Click</button> {value && <div>Div</div>} </form> ) }
原测试代码
import { render, screen } from "@testing-library/react" import NewForm from "./NewForm" describe('', () => { it('', async () => { render(<NewForm/>) screen.debug() const divText = await screen.findByText(/div/i) expect(divText).toBeInTheDocument() screen.debug() }) })
错误原因
测试超时失败,核心原因是findByText默认超时时间为1000ms,和组件中setTimeout的延迟时间完全一致,异步调度的微小延迟会导致测试未等到元素渲染就触发超时。
修复方案及代码
方案1:延长findByText超时时间
显式设置更长的超时时间,确保测试能等到元素完成渲染:
import { render, screen } from "@testing-library/react" import NewForm from "./NewForm" describe('NewForm async test', () => { it('renders Div text after 1s', async () => { render(<NewForm/>) // 延长超时时间至1500ms,覆盖组件的1秒延迟 const divText = await screen.findByText(/div/i, { timeout: 1500 }) expect(divText).toBeInTheDocument() }) })
方案2:使用Fake Timers模拟时间(推荐)
借助jest的fake timers跳过真实等待,大幅提升测试效率:
import { render, screen, waitFor } from "@testing-library/react" import NewForm from "./NewForm" describe('NewForm async test', () => { it('renders Div text after 1s', async () => { // 启用fake timers jest.useFakeTimers() render(<NewForm/>) // 快进1秒时间,触发setTimeout回调 jest.advanceTimersByTime(1000) // 等待状态更新和DOM渲染完成 await waitFor(() => { expect(screen.getByText(/div/i)).toBeInTheDocument() }) // 恢复真实timers,避免影响其他测试 jest.useRealTimers() }) })
更简洁的写法:
import { render, screen } from "@testing-library/react" import NewForm from "./NewForm" describe('NewForm async test', () => { it('renders Div text after 1s', async () => { jest.useFakeTimers() render(<NewForm/>) // 执行所有待触发的timers jest.runAllTimers() // 等待DOM更新 const divText = await screen.findByText(/div/i) expect(divText).toBeInTheDocument() jest.useRealTimers() }) })
内容的提问来源于stack exchange,提问作者Ник Белый
相关产品推荐
相关产品推荐

