You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用Mocha/Chai/Sinon测试异步image.onload处理器

我之前也碰到过类似的异步回调测试问题,尤其是image.onload这种依赖浏览器事件的场景,结合你用到的Sinon、Mocha和Chai,给你几个实用的解决思路:

解决Image.onload异步测试的核心方案

1. 手动模拟Image对象并主动触发onload

浏览器里的Image加载是异步的,测试环境中我们可以直接模拟一个Image实例,跳过真实的网络请求,手动触发它的onload回调来验证逻辑。

示例代码:

describe('RenderObject', () => {
  let sandbox;
  let mockImage;

  beforeEach(() => {
    sandbox = Sinon.createSandbox();
    // 构造一个模拟的Image对象,预设图片尺寸
    mockImage = {
      src: '',
      onload: null,
      width: 100,
      height: 200
    };
    // 替换全局的Image构造函数,让RenderObject创建我们的模拟实例
    sandbox.stub(global, 'Image').returns(mockImage);
  });

  afterEach(() => {
    // 测试后恢复所有stub,避免污染其他测试
    sandbox.restore();
  });

  it('should set correct dimensions for objects after image loads', (done) => {
    // 创建RenderObject实例
    const renderObj = new RenderObject('test-image.png');
    
    // 验证初始状态下尺寸未被设置
    expect(renderObj.width).to.be.undefined;
    expect(renderObj.height).to.be.undefined;

    // 手动触发onload回调,模拟图片加载完成
    mockImage.onload();

    // 验证尺寸是否被正确赋值
    expect(renderObj.width).to.equal(100);
    expect(renderObj.height).to.equal(200);
    done(); // 告诉Mocha异步测试已完成
  });
});

2. 用Sinon Fake Timers同步处理异步流程

如果你的代码依赖浏览器事件循环,Sinon的Fake Timers可以帮你把异步回调同步执行,不用依赖done()函数。

示例代码:

it('should update dimensions synchronously using fake timers', () => {
  const clock = Sinon.useFakeTimers();
  const mockImage = {
    src: '',
    onload: null,
    width: 150,
    height: 150
  };
  Sinon.stub(global, 'Image').returns(mockImage);

  const renderObj = new RenderObject('test.png');
  
  // 触发onload并强制执行所有待处理的回调
  mockImage.onload();
  clock.tick(0);

  // 验证尺寸设置正确
  expect(renderObj.width).to.equal(150);
  expect(renderObj.height).to.equal(150);

  // 清理资源
  clock.restore();
  Sinon.restore();
});

3. 验证处理器函数的绑定与调用

有时候问题可能出在构造函数的绑定逻辑上,你可以用Sinon Spy来确认处理器函数是否在onload时被正确调用。

示例代码:

it('should bind the dimension handler to image.onload', () => {
  // 假设你的尺寸设置逻辑是RenderObject的原型方法setDimensions
  const setDimensionsSpy = Sinon.spy(RenderObject.prototype, 'setDimensions');
  const mockImage = {
    src: '',
    onload: null
  };
  Sinon.stub(global, 'Image').returns(mockImage);

  // 创建实例,触发onload
  new RenderObject('test.jpg');
  mockImage.onload();

  // 验证处理器函数被调用了一次
  assert(setDimensionsSpy.calledOnce);
  
  // 清理spy和stub
  setDimensionsSpy.restore();
  Sinon.restore();
});

额外注意事项

  • 如果是在Node.js环境下测试,需要用jsdom模拟浏览器环境,这样全局的Image对象才会存在,才能被Sinon stub。
  • 若你的逻辑是更新所有使用同一图片源的对象,测试时要创建多个RenderObject实例,验证它们的尺寸都被正确更新。
  • 务必在每个测试后恢复Sinon的stubs/spies,避免测试用例之间的状态污染。

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

火山引擎 最新活动