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

使用Jest与React Testing Library测试图片尺寸校验组件时image.onload无法完成的问题求助

解决Jest/React Testing Library中Image.onload不触发及尺寸获取问题

在Jest的Node.js测试环境中,原生的Image对象并没有真实的浏览器实现,所以默认情况下image.onload不会触发,直接模拟也会导致width/height为undefined。我们可以通过正确模拟Image类+修复组件代码中的小问题来解决这个问题。

第一步:修复组件中的代码漏洞

你的checkDimension函数里,image.onerror的赋值放在了onload回调内部,这会导致如果图片加载失败,onerror永远不会被绑定,Promise会一直处于pending状态。先把这个问题修复:

const checkDimension = async (result) => {
  const image = new Image();
  const promiseImg = new Promise(
    (resolve, reject) => {
      console.log('Started');
      // 正确绑定onload
      image.onload = () => {
        resolve(image.height <= 100 && image.width <= 100);
      };
      // 将onerror移到外部,确保加载失败时能触发reject
      image.onerror = (error) => reject(error);
    });
  image.src = result;
  const isValid = await promiseImg;
  console.log('finished with result', isValid);
  return isValid;
};

第二步:在Jest中模拟带有尺寸的Image对象

我们需要自定义一个MockImage类,它能根据测试用的图片数据设置对应的宽高,并自动触发onload。可以通过识别测试中使用的base64字符串来区分不同的图片尺寸:

import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Avatar from './avatar';

describe('Avatar test dimensions', () => {
  let uploader;
  let originalImage;

  beforeAll(() => {
    // 保存原生Image,测试结束后恢复
    originalImage = global.Image;
    global.Image = class MockImage {
      constructor() {
        this.onload = null;
        this.onerror = null;
        this.width = 0;
        this.height = 0;
      }

      // 通过setter拦截src赋值,根据base64内容设置对应尺寸
      set src(dataUrl) {
        // 匹配第一个测试用的102x1图片的base64前缀
        if (dataUrl.includes('iVBORw0KGgoAAAANSUhEUgAAAGYAAAABCAYAAAAsLtu')) {
          this.width = 102;
          this.height = 1;
        } 
        // 匹配第二个测试用的2x2图片的base64前缀
        else if (dataUrl.includes('iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0k')) {
          this.width = 2;
          this.height = 2;
        }
        // 异步触发onload,模拟浏览器加载图片的异步行为
        setTimeout(() => {
          if (this.onload) this.onload();
        }, 0);
      }
    };
  });

  afterAll(() => {
    // 恢复原生Image,避免影响其他测试
    global.Image = originalImage;
  });

  beforeEach(() => {
    render(<Avatar />);
    uploader = screen.getByTestId('imageInput');
  });

  it('should give error for wrong dimension of real image file with size 102x1px', async ()=>{
    const file = new File(
      ['iVBORw0KGgoAAAANSUhEUgAAAGYAAAABCAYAAAAsLtuAAAAAEklEQVR42mNkYPhfzzAKBh0AAGClAYCUny1uAAAAAElFTkSuQmCC'],
      'image102x1.png',
      { type: 'image/png' }
    );
    fireEvent.change(uploader, {target: { files: [file] } });
    expect(await screen.findByText('Wrong dimension')).toBeInTheDocument();
  });

  it('should pass real image less than equal 100x100',async ()=>{
    const file = new File(['iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAEklEQVR42mP8/5/hPwMQMMIYAELqBf31meVMAAAAAElFTkSuQmCC'], 'image2x2.png', { type: 'image/png' });
    fireEvent.change(uploader, {target: { files: [file] } });
    expect(await screen.findByText('Good dimension')).toBeInTheDocument();
  });
});

其他可选方案

如果你的测试用例更多,或者不想通过base64字符串判断,可以:

  • 给测试用的File对象添加自定义属性(比如customWidth/customHeight),然后在组件的handleImageLoad中把这些属性传递给checkDimension,再在模拟Image时使用这些值。
  • 使用jest.spyOn动态设置Image的宽高,针对每个测试用例单独配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 13:37:41