如何在单元测试中触发FileReader的`onloadend`?
How to trigger FileReader's `onloadend` in unit tests?
我正在尝试使用反应测试库测试 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 behaviour,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);
我正在尝试使用反应测试库测试 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 behaviour,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);