You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 17:05:45