如何使用jest模拟回调函数

28

我正在尝试使用jest来模拟一个自定义函数,但是遇到了问题。

这是我的函数:

export const resizeImage = (file, fileName, callback) => {
  const MAX_WIDTH = avatarImage.maxWidth;
  const MAX_HEIGHT = avatarImage.maxHeight;
  const img = document.createElement('img');
  img.src = window.URL.createObjectURL(file);
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');

  img.onload = () => {
    const sizes = internalResizeImage(img, MAX_WIDTH, MAX_HEIGHT);

    canvas.width = sizes.width;
    canvas.height = sizes.height;
    ctx.drawImage(img, 0, 0, sizes.width, sizes.height);
    return callback(dataURItoFile(canvas.toDataURL(), fileName));
  };
};

我这样打电话:

resizeImage(acceptedFiles[0], this.props.user.id, (res) => {
  //dostuff
});

在我的测试中,我是这样模拟它的:

let mockResizeImage = jest.fn();

jest.mock('../../utilities/imageUtils', () => ({
  resizeImage: () => mockResizeImage
}));

我希望mockResizeImage成为一个回调函数,然后在我的测试中更改返回的值:

it('should call handleDrop and accept files', () => {
    //mockResizeImage.mockReturnValue('something');

    const instance = shallow(mockComponent()).instance();
    const acceptFilesMock = ['test'];

    instance.handleDrop(acceptFilesMock);

    expect(clickSpy).toHaveBeenCalledTimes(1);
  });

如果这是一个承诺就太好了,但它是一个回调函数,我不知道自己做错了什么。

谢谢。

2个回答

54

你可以使用一个接受与原函数相同参数的函数来模拟一个模块,并立即调用回调函数:

jest.mock('../../utilities/imageUtils', () => ({
  resizeImage: (file, fileName, callback) => callback('someData')
}));

顺便说一下,你在问题中嘲笑模块的方式是行不通的,因为 jest.mock 的工作方式。即使你在 let 语句之后编写它,当测试被编译时,它也会被提升到文件的顶部。因此,使用间谍来模拟函数的最佳方法如下:

import {resizeImage} from '../../utilities/imageUtils'

jest.mock('../../utilities/imageUtils', () => ({
  resizeImage: jest.fn((file, fileName, callback) => callback('someData'))
}));

现在你拥有与上面相同的行为,但你也可以测试resizeImage是否使用了正确的参数。

由于您的最后一个参数是一个函数,因此您可以像这样仅测试前两个参数,使用mock.calls

expect(resizeImage.mock.calls[0][0]).toBe('firstParameter')
expect(resizeImage.mock.calls[0][1]).toBe('secondParameter')

或者当使用expect.anything()时,对于最后一个参数使用通配符来使用toBeCalledWith:

expect(resizeImage).toBeCalledWith('firstParameter', 'secondParameter', expect.anything()); 

非常感谢你,Andreas!它可以工作,但我不知道如何测试resizeImage导入。我需要添加一个spider吗?谢谢。 - Albert Olivé Corbella
你说的“测试resizeImage”是什么意思?是指测试resizeImage是否使用了正确的参数调用吗? - Andreas Köberle
是的,我现在这样做了:expect(resizeImage).toHaveBeenCalledTimes(1); - Albert Olivé Corbella
1
更新了我的回答。 - Andreas Köberle
我只使用expect(resizeImage.mock.calls[0][0]).toBe('firstParameter')而不是调用回调函数的模拟,这样可以吗? - Robbie Cook

4

确保在通过将回调函数作为参数之一调用实际函数时,该函数是从测试块内部调用的,如下所示

function forEach(items, callback) {
  for (let index = 0; index < items.length; index++) {
    callback(items[index]);
  }
}
const mockCallback = jest.fn((x) => x + 1);

test("fn", () => {
  forEach([0, 1, 2], mockCallback);
  expect(mockCallback.mock.calls.length).toBe(3);
});

而不是像以下这样

function forEach(items, callback) {
      for (let index = 0; index < items.length; index++) {
        callback(items[index]);
      }
    }
const mockCallback = jest.fn((x) => x + 1);
forEach([0, 1, 2], mockCallback);
    
test("fn", () => {
   expect(mockCallback.mock.calls.length).toBe(3);
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接