Javascript 如何使用Jest测试img.onload?

Javascript 如何使用Jest测试img.onload?,javascript,unit-testing,jestjs,Javascript,Unit Testing,Jestjs,我一直在进行img.onload事件测试。我知道这是一个异步操作,可能需要模拟,但我仍然不知道如何解决这个问题。我也看到过一些类似的案例,但它们与这次不同 以前访问过: 要测试的代码: function funcToTest(img, callback) { const img = new Image() img.src = img img.onload = () => { callback(true) // should return

我一直在进行
img.onload
事件测试。我知道这是一个异步操作,可能需要模拟,但我仍然不知道如何解决这个问题。我也看到过一些类似的案例,但它们与这次不同

以前访问过:

要测试的代码:

  function funcToTest(img, callback) {
    const img = new Image()
    img.src = img

    img.onload = () => {
      callback(true) // should return callback with true on finish
    }

    img.onerror = (e) => {
      callback(false) // should return callback with false on error
      console.log(e) 
    }
  }

  funcToTest()
测试环境:

describe('tet it', () => {
  it('test', done => {
    const callback = status => {
      expect(status).toEqual(true) // but nothing happen
      done()
    }

    funcToTest('some_image', callback)
  })
})
此外,我在完成时遇到一个错误:

    Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
        > 2 |   it('test', done => {...


提前谢谢

要测试的功能:

index.ts

import { funcToTest } from './';

describe('57092154', () => {
  let onloadRef: Function | undefined;
  let onerrorRef: Function | undefined;
  beforeAll(() => {
    Object.defineProperty(Image.prototype, 'onload', {
      get() {
        return this._onload;
      },
      set(onload: Function) {
        onloadRef = onload;
        this._onload = onload;
      },
    });
    Object.defineProperty(Image.prototype, 'onerror', {
      get() {
        return this._onerror;
      },
      set(onerror: Function) {
        onerrorRef = onerror;
        this._onerror = onerror;
      },
    });
  });
  it('should handle onload event', () => {
    const callback = jest.fn();
    funcToTest('./test.png', callback);
    onloadRef!();
    expect(callback).toBeCalledWith(true);
  });

  it('should handle onerror event', () => {
    const callback = jest.fn();
    const logSpy = jest.spyOn(console, 'log').mockImplementation(() => 'suppress error');
    funcToTest('./test.png', callback);
    const mErr = new Error('network');
    onerrorRef!(mErr);
    expect(callback).toBeCalledWith(false);
    expect(logSpy).toBeCalledWith(mErr);
  });
});
函数funcToTest(imgUrl:string,callback:function){
const img=新图像();
img.src=imgUrl;
img.onload=()=>{
回调(true);
};
img.onerror=e=>{
回调(假);
控制台日志(e);
};
返回img;
}
导出{funtotest};
  • 单元测试解决方案1:
/**
*@jest-environment-jsdom
*/
从“/”导入{funtotest};
描述('测试套件',()=>{
它('onload',done=>{
const callback=jest.fn(状态=>{
expect(status).toBe(true);
完成();
});
常量imageUrl=https://github.com/mrdulin';
const img=funcToTest(imageUrl,回调);
如果(img.onload){
const事件:any={};
img.onload(事件);
}
});
它('onerror',done=>{
const consoleLogSpyOn=jest.spyOn(控制台,'log');
const callback=jest.fn(状态=>{
expect(status).toBe(false);
完成();
});
常量imageUrl=https://github.com/mrdulin';
const img=funcToTest(imageUrl,回调);
如果(如有错误){
const event:any={message:'some error'};
img.onerror(事件);
expect(consoleLogSpyOn).与(事件)一起调用;
}
consoleLogSpyOn.mockRestore();
});
});
单元测试结果和覆盖率:

PASS src/stackoverflow/57092154/index.spec.ts
测试套件
✓ 空载(8ms)
✓ 误差(8ms)
console.log node_modules/jest mock/build/index.js:860
{消息:“某些错误”}
----------|----------|----------|----------|----------|-------------------|
文件|%Stmts |%Branch |%Funcs |%Line |未覆盖行|s|
----------|----------|----------|----------|----------|-------------------|
所有文件| 100 | 100 | 100 | 100 ||
index.ts | 100 | 100 | 100 | 100 ||
----------|----------|----------|----------|----------|-------------------|
测试套件:1个通过,共1个
测试:2次通过,共2次
快照:共0个
时间:3.821s
以下是完整的演示:

更新:未删除的答案,单元测试解决方案2

您可以使用方法为
Image.prototype.onload
方法创建getter和setter。然后,您可以在测试用例中获取
onload
函数并手动执行它

index.test.ts

import { funcToTest } from './';

describe('57092154', () => {
  let onloadRef: Function | undefined;
  let onerrorRef: Function | undefined;
  beforeAll(() => {
    Object.defineProperty(Image.prototype, 'onload', {
      get() {
        return this._onload;
      },
      set(onload: Function) {
        onloadRef = onload;
        this._onload = onload;
      },
    });
    Object.defineProperty(Image.prototype, 'onerror', {
      get() {
        return this._onerror;
      },
      set(onerror: Function) {
        onerrorRef = onerror;
        this._onerror = onerror;
      },
    });
  });
  it('should handle onload event', () => {
    const callback = jest.fn();
    funcToTest('./test.png', callback);
    onloadRef!();
    expect(callback).toBeCalledWith(true);
  });

  it('should handle onerror event', () => {
    const callback = jest.fn();
    const logSpy = jest.spyOn(console, 'log').mockImplementation(() => 'suppress error');
    funcToTest('./test.png', callback);
    const mErr = new Error('network');
    onerrorRef!(mErr);
    expect(callback).toBeCalledWith(false);
    expect(logSpy).toBeCalledWith(mErr);
  });
});
单元测试结果:

 PASS  examples/57092154/index.test.ts
  57092154
    ✓ should handle onload event (4 ms)
    ✓ should handle onerror event

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.ts |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        3.741 s

虽然有人回答了这个问题,但我不同意解决方案的目的

我们不应该为了通过覆盖率而编写测试,我们应该编写能够证明某一点的测试,并确保代码的行为符合预期

测试它的方法是模拟映像构造函数,并用调用
onload
函数的东西替换它

description('tet it',()=>{
它('test',done=>{
global.Image=class{
构造函数(){
设置超时(()=>{
this.onload();//模拟成功
}, 100);
}
}
常量回调=状态=>{
完成()
}
funcToTest('some_image',回调)
})
})
该方法仅假设“浏览器”将在100毫秒内下载图像,如果需要在测试之间共享此行为,您可以调整失败的代码,或者在每次测试之前将部分代码移动到

如果您在“s
onload
事件处理程序中有一些逻辑,并且希望测试它是否正确应用,实际上,您可以使用以编程方式创建图像

考虑这一功能:

const imageDimensions = (
  file: File
): Promise<{ width: number; height: number }> =>
  new Promise((resolve, reject) => {
    const img = new Image()

    img.onload = () => {
      const { naturalWidth: width, naturalHeight: height } = img
      resolve({ width, height })
    }

    img.onerror = () => {
      reject('There is something wrong with this image')
    }

    img.src = URL.createObjectURL(file)
  })`
在本例中,将创建画布,然后将其转换为blob,这类似于用户选择某个图像文件时由浏览器生成的blob


另外,这是Jest和Jasmine兼容的。

可能看起来很傻,但您是否尝试过将
img.src=img
移动到
funcToTest
的末尾,即在定义了
onload
/
onerror
函数之后?为什么要测试img.onload?那是你维护的代码吗?测试您不维护的代码似乎是一个错误。@DovRine,因为我需要在回调工作结束时测试它。这就是为什么。@MaxTravis:为什么你不能测试回调呢?img.onload是否提供回调所需的参数?不仅如此,这样做可能会大大降低您的测试速度。@MaxTravis:Image是您正在测试的类吗?看起来被测试的函数是FuncToTest。我认为您应该模拟图像,使其具有onload和onerror事件,以便测试FuncToTest。太棒了!谢谢,先生!同时,此测试不会使任何img负载像
.onload
事件上的“真正测试”。它只调用提供的回调,因为实际上我们调用了
img.onload
事件。不幸的是,它没有任何意义……这里的问题是,您可以从funcToTest中删除onload调用,并且测试将通过,在测试时,测试不应该有任何关于工作方式的内部知识-回调可能会更改为setTimeout或任何其他错误:“构造签名的类型不兼容。类型
new()=>Image
不可分配给类型
new(宽度?:数字,高度?:数字)=>HTMLImageElement
。Type
Image
缺少Type
HTMLImageElement
中的以下属性:align、alt、border、complete等。“@Jago这是一个typescript错误,上面的代码不是用安全的typescript编写的。mock