如何使用Jest测试img.onload?

时间:2019-07-18 10:12:32

标签: javascript unit-testing jestjs

我坚持进行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 => {...

谢谢!

3 个答案:

答案 0 :(得分:1)

这是我的解决方法:

index.ts

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

  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);
  };

  return img;
}

export { funcToTest };

单元测试:

/**
 * @jest-environment jsdom
 */

import { funcToTest } from './';

describe('test suites', () => {
  it('onload', done => {
    const callback = jest.fn(status => {
      expect(status).toBe(true);
      done();
    });

    const imageUrl = 'https://github.com/mrdulin';
    const img = funcToTest(imageUrl, callback);
    if (img.onload) {
      const event: any = {};
      img.onload(event);
    }
  });

  it('onerror', done => {
    const consoleLogSpyOn = jest.spyOn(console, 'log');
    const callback = jest.fn(status => {
      expect(status).toBe(false);
      done();
    });

    const imageUrl = 'https://github.com/mrdulin';
    const img = funcToTest(imageUrl, callback);
    if (img.onerror) {
      const event: any = { message: 'some error' };
      img.onerror(event);
      expect(consoleLogSpyOn).toBeCalledWith(event);
    }
    consoleLogSpyOn.mockRestore();
  });
});

单元测试结果和覆盖范围:

 PASS  src/stackoverflow/57092154/index.spec.ts
  test suites
    ✓ onload (8ms)
    ✓ onerror (8ms)

  console.log node_modules/jest-mock/build/index.js:860
    { message: 'some error' }

----------|----------|----------|----------|----------|-------------------|
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.821s

以下是完整的演示:https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/57092154

答案 1 :(得分:1)

在回答此问题时,我不同意解决方案的目的。

我们不应该编写测试以通过覆盖率,而应该编写证明要点的测试,并确保代码片段表现出预期的效果。

测试它的方法是模拟Image构造函数并将其替换为将调用onload函数的东西。

describe('tet it', () => {

  it('test', done => {
    global.Image = class {
      constructor() {
        setTimeout(() => {
          this.onload(); // simulate success
        }, 100);
      }
    }

    const callback = status => {
      done()
    }

    funcToTest('some_image', callback)
  })
})

该方法仅假设“浏览器”将在100毫秒内下载图像,您可以调整代码以进行故障处理,或者将其中的一部分移至beforeEach,如果需要在测试之间共享此行为。

答案 2 :(得分:1)

如果您在Imageonload事件处理程序中有逻辑,并且想测试它是否正确应用,则可以使用{{3}以编程方式实际创建图像blob }。

考虑此功能:

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)
  })`

因此,给定Canvas(blob),它将返回一个File,它解析为具有尺寸的对象。或因错误而拒绝。

我们如何测试onload部分?您可以在测试中以编程方式创建blob,但是new Blob(['a'.repeat(10)], { type: 'image/jpeg' })不会触发onload,因为这实际上不是图像。

无需模拟任何东西,使用JSDOM或类似的东西。帆布救援!可以这样简单地进行测试:

describe('imageDimensions', () => {
  it('should resolve with correct dimensions given an IMAGE blob', done => {
    // although this canvas has no actual graphics, it is still an image and it contains image metadata, thus onload() will be fired
    const canvas = document.createElement('canvas')
    canvas.width = 10
    canvas.height = 10

    canvas.toBlob(
      async blob => {
        const { width, height } = await component.imageDimensions(blob)
        expect(width).toBe(10)
        expect(height).toBe(10)
        done()
      },
      'image/jpeg',
      0.1
    )
  })

  it('should reject with an error if the file provided does NOT seem to be an image', async () => {
    const file = new Blob(['a'.repeat(10)], { type: 'application/pdf' })

    try {
      await component.imageDimensions(file)
    } catch (error) {
      expect(error).toBe('There is something wrong with this image')
    }
  })
})

在此示例中,将创建Canvas,然后将其转换为blob,类似于用户选择某些图像文件时浏览器生成的blob。

P.S。这是Jest和Jasmine兼容的。