React Testing Library测试useEffect内setState及定时器
离线提示组件测试实现方案
先提你代码里的一个显性bug:定时器清理逻辑误用了clearInterval,你创建延时任务用的是setTimeout,这里要改成clearTimeout(timer),不然清理逻辑本身就不生效,测试结果会不符合预期。
测试全程用Jest自带的Fake Timer能力即可,不需要额外依赖,测试文件开头先开启假定时器:
jest.useFakeTimers()
同时提前mock好useNetInfo的返回值,支持手动修改网络状态触发组件重渲染即可。
对应你三个疑问的测试实现
测试网络恢复时的横幅隐藏逻辑
不管是isConnected变回true,还是isInternetReachable变回true,隐藏逻辑都是同步触发的,不需要等待定时器。状态变更操作包在act()中保证React完成渲染后,直接断言横幅不存在即可:test('网络状态恢复时自动隐藏离线横幅', () => { // 初始化为断网状态,横幅默认显示 mockUpdateNetInfo({ isConnected: false, isInternetReachable: false }) const { queryByText } = render( <OfflineModeContainer> <Text>页面内容</Text> </OfflineModeContainer> ) expect(queryByText('aucune connexion internet.')).toBeTruthy() // 触发isConnected恢复为true act(() => { mockUpdateNetInfo({ isConnected: true }) }) expect(queryByText('aucune connexion internet.')).toBeFalsy() // 测试isInternetReachable恢复的场景 act(() => { mockUpdateNetInfo({ isConnected: false, isInternetReachable: false }) jest.advanceTimersByTime(3000) // 等3秒触发横幅显示 }) expect(queryByText('aucune connexion internet.')).toBeTruthy() act(() => { mockUpdateNetInfo({ isInternetReachable: true }) }) expect(queryByText('aucune connexion internet.')).toBeFalsy() })测试
setTimeout3秒延迟显示逻辑
不需要真实等待3秒,用jest.advanceTimersByTime手动控制时间推进即可,精准验证时间阈值前后的显示状态:test('isInternetReachable为false满3秒才显示横幅', () => { // 初始状态网络正常 mockUpdateNetInfo({ isConnected: true, isInternetReachable: true }) const { queryByText } = render( <OfflineModeContainer> <Text>页面内容</Text> </OfflineModeContainer> ) expect(queryByText('aucune connexion internet.')).toBeFalsy() // 触发网络不可达 act(() => { mockUpdateNetInfo({ isInternetReachable: false }) }) // 时间推进2999毫秒,还没到阈值,横幅不显示 act(() => { jest.advanceTimersByTime(2999) }) expect(queryByText('aucune connexion internet.')).toBeFalsy() // 再推进1毫秒,满3秒,横幅显示 act(() => { jest.advanceTimersByTime(1) }) expect(queryByText('aucune connexion internet.')).toBeTruthy() })测试定时器清理逻辑
核心验证点是:3秒阈值到达前如果网络恢复,定时器会被清除,不会后续触发横幅显示。可以通过spy监听clearTimeout的调用做断言:test('网络提前恢复时清除定时器,不会延迟显示横幅', () => { const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout') mockUpdateNetInfo({ isConnected: true, isInternetReachable: true }) const { queryByText } = render( <OfflineModeContainer> <Text>页面内容</Text> </OfflineModeContainer> ) // 触发网络不可达 act(() => { mockUpdateNetInfo({ isInternetReachable: false }) }) // 1秒后网络提前恢复 act(() => { jest.advanceTimersByTime(1000) mockUpdateNetInfo({ isInternetReachable: true }) }) // 断言清理定时器的逻辑被调用 expect(clearTimeoutSpy).toHaveBeenCalled() // 时间推进到3秒后,横幅也不会显示 act(() => { jest.advanceTimersByTime(3000) }) expect(queryByText('aucune connexion internet.')).toBeFalsy() clearTimeoutSpy.mockRestore() })
现有测试用例报错原因
你贴的失败用例有两个问题:
queryByText是同步查询方法,不会返回Promise,前面加await拿到的是RTL返回的元素包装对象,不是null,自然过不了toBeFalsy断言- 状态更新、时间推进的操作没有包在
act()中,React没完成渲染就执行断言,结果不符合预期
内容的提问来源于stack exchange,提问作者Dimitri Kopriwa
相关产品推荐
相关产品推荐

