如何在单元测试中触发FileReader的`onloadend`?

Jir*_*Jir 6 javascript unit-testing reactjs jestjs react-testing-library

我正在尝试input type=file使用反应测试库来测试组件。

该组件是一个标准<input>元素,具有以下功能来处理图像提交:

export default function ImageUpload(props) {
  const { image, setImage } = props;
  const handleImageChange = e => {
    e.preventDefault();
    let reader = new FileReader();
    const imageFile = e.target.files[0];
    reader.onloadend = () => {
      const image = reader.result;
      setImage(image);
    };
    reader.readAsDataURL(imageFile);
  };
  // etc.
}
Run Code Online (Sandbox Code Playgroud)

因为我想模拟图像的上传,所以我开始这样测试:

test("ImageUpload shows two buttons after an image has been uploaded", () => {
    const setImageSpy = jest.fn();
    const image = "data:image/jpeg;base64,/9j/4AAQSkZJ//20==";
    const file = new File([image], "chucknorris.jpg", { type: "image/jpeg" });

    const readAsDataURL = jest.fn();
    const onloadend = jest.fn();
    jest.spyOn(global, "FileReader")
      .mockImplementation(function() {
        this.readAsDataURL = readAsDataURL;
        this.onloadend = onloadend;
      });

    const { getByTestId } = render(
      <ImageUpload image={image} setImage={setImageSpy} />
    );
    fireEvent.change(getByTestId("ImageUpload"), {
      target: {
        files: [file]
      }
    });
    expect(setImageSpy).toHaveBeenCalledWith(image);  // this fails
    expect(readAsDataURL).toHaveBeenCalledTimes(1);
    expect(readAsDataURL).toHaveBeenCalledWith(file);
  });
Run Code Online (Sandbox Code Playgroud)

问题是setImageSpy永远不会被调用。如果我理解正确的话,这是因为onloadend永远不会被触发。

我怎样才能触发该事件?

Est*_*ask 8

根据预期的行为readAsDataURL模拟应该提供result而不是存根。

this.onloadend = onloadend是朝着错误方向迈出的一步。onloadend不应该被嘲笑,因为它是在经过测试的代码中分配的。测试时需要手动调用:

jest.spyOn(global, "FileReader")
  .mockImplementation(function() {
    this.readAsDataURL = jest.fn(() => this.result = image);
  });

...

expect(FileReader).toHaveBeenCalledTimes(1);

const reader = FileReader.mock.instances[0];

expect(reader.readAsDataURL).toHaveBeenCalledTimes(1);
expect(reader.readAsDataURL).toHaveBeenCalledWith(file);
expect(reader.onloadend).toEqual(expect.any(Function));

expect(setImageSpy).not.toHaveBeenCalled();

act(() => reader.onloadend());

expect(setImageSpy).toHaveBeenCalledWith(image);
Run Code Online (Sandbox Code Playgroud)