React AsyncImage组件单元测试:如何触发load事件渲染图片
问题:AsyncImage组件单元测试无法触发handleLoad函数
我使用以下AsyncImage异步图片组件:
import { useEffect, useState } from 'react' const AsyncImage = (props) => { const [loadedSrc, setLoadedSrc] = useState(null) useEffect(() => { setLoadedSrc(null) if (props.src) { const handleLoad = () => { setLoadedSrc(props.src) } const image = new Image() image.addEventListener('load', handleLoad) image.src = props.src return () => { image.removeEventListener('load', handleLoad) } } }, [props.src]) if (loadedSrc === props.src) { return ( <img {...props} /> ) } return null } export default AsyncImage
编写的单元测试代码如下:
global.Image = class { constructor() { this.onload = jest.fn(); this.addEventListener = jest.fn() this.removeEventListener = jest.fn() setTimeout(() => { this.onload(); }, 50); } }; test("render AsyncImage", async () => { const logo = LOAD_SUCCESS_SRC let container await act(async () => { container = render( <Router> <Provider store={store}> <AsyncImage src={logo} alt="test.png" className='img-fluid img-catalogCards' /> </Provider> </Router> ) }) })
测试中模拟了global.Image类,代码会进入setTimeout,但始终无法进入组件的handleLoad函数。请问如何触发图片加载时的handleLoad函数,使组件进入图片渲染分支?
解决方案
问题出在你的Image模拟实现上:组件通过addEventListener('load', handleLoad)注册加载回调,但你当前的模拟只调用了this.onload(),并没有触发通过addEventListener注册的load事件回调。
修改模拟的Image类,存储注册的事件回调,然后在定时器里触发对应的load事件:
global.Image = class { constructor() { // 存储事件回调的映射 this.eventListeners = {}; this.addEventListener = jest.fn((event, callback) => { if (!this.eventListeners[event]) { this.eventListeners[event] = []; } this.eventListeners[event].push(callback); }); this.removeEventListener = jest.fn((event, callback) => { if (this.eventListeners[event]) { this.eventListeners[event] = this.eventListeners[event].filter(cb => cb !== callback); } }); // 模拟图片加载完成,触发load事件的回调 setTimeout(() => { if (this.eventListeners['load']) { this.eventListeners['load'].forEach(callback => callback()); } }, 50); } };
同时,调整测试逻辑,使用waitFor等待组件异步更新完成并渲染图片:
test("render AsyncImage", async () => { const logo = LOAD_SUCCESS_SRC render( <Router> <Provider store={store}> <AsyncImage src={logo} alt="test.png" className='img-fluid img-catalogCards' /> </Provider> </Router> ) // 等待图片元素渲染出来 await waitFor(() => { expect(screen.getByAltText('test.png')).toBeInTheDocument(); }); })
核心改动说明:
- 新增
eventListeners对象存储通过addEventListener注册的回调,确保能准确触发组件绑定的load事件处理函数 - 移除无用的
this.onload模拟,因为组件未使用该属性 - 使用
waitFor替代手动act,符合React Testing Library的最佳实践,确保等待组件完成异步更新
内容的提问来源于stack exchange,提问作者Laura
相关产品推荐
相关产品推荐

