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

使用Jest测试React自定义图片加载Hook时,如何触发onload函数?

解决useImage Hook中onload函数无法被Jest测试的问题

问题背景

我尝试测试用于加载图片的自定义Hook的onload函数,但运行测试时onload被跳过。

自定义Hook代码

import { useEffect, useState } from "react";

const useImage = (src: string) => {
    const [loaded, setLoaded] = useState(false);

    useEffect(() => {
        const image = new Image();
        image.src = src;
        image.onload = () => {
            return setLoaded(true); // 无法触发该逻辑
        };
    }, [src]);

    return { loaded };
};

export default useImage;

测试代码

import {
    renderHook,
    waitFor,
} from "@testing-library/react";
import useImage from "./useImage";

describe("useImage", () => {

    it("Loads a data url into an image element", async () => {

        const img = new Image();
        img.src = "foo";

        const { result } = renderHook(() => useImage("foo"));

        await waitFor(() =>
            expect(result.current).toStrictEqual({ loaded: true }) // loaded始终为false
        );

    });
});

解决方案

Jest运行在Node.js环境中,没有真实浏览器的图片加载能力,原生Image对象的onload事件不会自动触发,需要通过Mock的方式手动触发或模拟加载完成。

方案一:手动捕获并触发onload回调

通过Mock全局Image构造函数,记录Hook中设置的onload回调,随后手动调用它:

import { renderHook, waitFor } from "@testing-library/react";
import useImage from "./useImage";

describe("useImage", () => {
  it("triggers onload and sets loaded to true", async () => {
    // 保存原始Image构造函数,测试后恢复
    const OriginalImage = global.Image;
    
    let mockOnload: (() => void) | undefined;
    // Mock Image类
    global.Image = jest.fn(() => ({
      set src(_: string) {
        // 不执行真实的图片加载逻辑
      },
      set onload(callback: () => void) {
        // 记录onload回调
        mockOnload = callback;
      },
    })) as typeof OriginalImage;

    const { result } = renderHook(() => useImage("foo"));

    // 手动触发onload回调
    mockOnload?.();

    await waitFor(() => {
      expect(result.current.loaded).toBe(true);
    });

    // 恢复原始Image构造函数
    global.Image = OriginalImage;
  });
});

方案二:模拟图片自动加载完成

MockImage构造函数,在实例创建后自动触发onload事件:

import { renderHook, waitFor } from "@testing-library/react";
import useImage from "./useImage";

describe("useImage", () => {
  it("sets loaded to true when image loads", async () => {
    // Mock Image,模拟图片立即加载完成
    jest.spyOn(global, "Image").mockImplementation(() => {
      const imgInstance = {} as HTMLImageElement;
      // 用setTimeout模拟异步加载完成
      setTimeout(() => imgInstance.onload?.(), 0);
      return imgInstance;
    });

    const { result } = renderHook(() => useImage("foo"));

    await waitFor(() => {
      expect(result.current.loaded).toBe(true);
    });

    // 恢复所有Mock
    jest.restoreAllMocks();
  });
});

内容的提问来源于stack exchange,提问作者NewJr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:07:41