我正在尝试使用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.
}
当我想模拟图像的上传时,我开始用这种方式进行测试:
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);
  });
问题是setImageSpy从未被调用。
如果我理解正确,那是因为onloadend永远不会被触发。
我该如何触发该事件?

最佳答案

根据expected behaviourreadAsDataURL模拟应该提供result而不是存根。this.onloadend = onloadend是朝错误方向迈出的一步。 onloadend不应被 mock ,因为它是在经过测试的代码中分配的。需要在测试中手动调用它:

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

10-06 04:18