在Jest中测试包含Promise和FileReader()的函数。

huangapple go评论87阅读模式
英文:

Testing functions containing Promise and FileReader() in Jest

问题

目前正在尝试测试以下函数:

const readFileAsync = (file, use = false) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      resolve(reader.result);
    };
    reader.onerror = reject;
    if (use) reader.readAsDataURL(file);
    else reader.readAsArrayBuffer(file);
  });

export default readFileAsync;

但在模拟FileReader方面遇到了大问题。有什么建议吗?

英文:

Currently trying to test the following function:

const readFileAsync = (file, use = false) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      resolve(reader.result);
    };
    reader.onerror = reject;
    if (use) reader.readAsDataURL(file);
    else reader.readAsArrayBuffer(file);
  });

export default readFileAsync;

But having massive issues with the FileReader aspect in regards to mocking. Any advice?

答案1

得分: 1

Here is the translated code portion you requested:

虽然我们模拟了 FileReader 的 onload 方法,但在执行 readFileAsync 方法后,reader.onload 被分配给了一个匿名函数(未模拟)。因此,我们需要暴露 reader 实例,获取测试用例中的 reader 实例并手动执行 reader.onload 方法。然后,承诺将使用模拟值 reader.result 解析。

例如:
index.ts

const readFileAsync = (file, use = false) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      resolve(reader.result);
    };
    reader.onerror = reject;
    if (use) reader.readAsDataURL(file);
    else reader.readAsArrayBuffer(file);
    (readFileAsync as any)._reader = reader;
  });

export default readFileAsync;

根据 (readFileAsync as any)._reader 暴露 reader 实例。

index.test.ts

import readFileAsync from './';

const mFileReader = jest.fn(() => {
  return {
    readAsDataURL: jest.fn(),
    readAsArrayBuffer: jest.fn(),
  };
});

(global as any).FileReader = mFileReader;

describe('59581721', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });
  afterAll(() => {
    jest.restoreAllMocks();
  });
  it('should read as data url correctly', async () => {
    const blob = new Blob(['a']);
    const pending = readFileAsync(blob, true);
    const mReader = (readFileAsync as any)._reader;
    mReader.result = 'mocked result';
    mReader.onload();
    const actual = await pending;
    expect(actual).toBe('mocked result');
    expect(mReader.readAsDataURL).toBeCalledWith(blob);
  });

  it('should read as array buffer corectly', async () => {
    const blob = new Blob(['a']);
    const pending = readFileAsync(blob);
    const mReader = (readFileAsync as any)._reader;
    mReader.result = 'mocked result';
    mReader.onload();
    const actual = await pending;
    expect(actual).toBe('mocked result');
    expect(mReader.readAsArrayBuffer).toBeCalledWith(blob);
  });
});

带覆盖率报告的单元测试结果:

 PASS  src/stackoverflow/59581721/index.test.ts (13.673s)
  59581721
    ✓ should read as data url correctly (21ms)
    ✓ should read as array buffer corectly (2ms)

----------|----------|----------|----------|----------|-------------------|
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:        15.648s

我的测试环境是 node

源代码链接:https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59581721

英文:

Although we mock FileReader onload method, after executing readFileAsync method, reader.onload is to assign to an anonymous function (not mocked). So we need to expose the reader instance, get the reader instance in the test case and execute reader.onload method manually. Then, the promise will be resolved with the mocked value of reader.result.

E.g.
index.ts:

const readFileAsync = (file, use = false) =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => {
      resolve(reader.result);
    };
    reader.onerror = reject;
    if (use) reader.readAsDataURL(file);
    else reader.readAsArrayBuffer(file);
    (readFileAsync as any)._reader = reader;
  });

export default readFileAsync;

Expose the reader instance according to (readFileAsync as any)._reader.

index.test.ts:

import readFileAsync from './';

const mFileReader = jest.fn(() => {
  return {
    readAsDataURL: jest.fn(),
    readAsArrayBuffer: jest.fn(),
  };
});

(global as any).FileReader = mFileReader;

describe('59581721', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });
  afterAll(() => {
    jest.restoreAllMocks();
  });
  it('should read as data url correctly', async () => {
    const blob = new Blob(['a']);
    const pending = readFileAsync(blob, true);
    const mReader = (readFileAsync as any)._reader;
    mReader.result = 'mocked result';
    mReader.onload();
    const actual = await pending;
    expect(actual).toBe('mocked result');
    expect(mReader.readAsDataURL).toBeCalledWith(blob);
  });

  it('should read as array buffer corectly', async () => {
    const blob = new Blob(['a']);
    const pending = readFileAsync(blob);
    const mReader = (readFileAsync as any)._reader;
    mReader.result = 'mocked result';
    mReader.onload();
    const actual = await pending;
    expect(actual).toBe('mocked result');
    expect(mReader.readAsArrayBuffer).toBeCalledWith(blob);
  });
});

Unit test result with coverage report:

 PASS  src/stackoverflow/59581721/index.test.ts (13.673s)
  59581721
    ✓ should read as data url correctly (21ms)
    ✓ should read as array buffer corectly (2ms)

----------|----------|----------|----------|----------|-------------------|
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:        15.648s

My testing environment is node.

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59581721

huangapple
  • 本文由 发表于 2020年1月4日 00:07:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/59581721.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定