如何在Jest中模拟更改以测试FileReader的onload事件?

4

SimpleDialog.jsx

const [imagePreview, setImagePreview] = React.useState(null);

const handleChangeImage = event => {
    let reader = new FileReader();
    let file = event.target.files[0];

    reader.onload = event => {
        console.log(event);

        setImagePreview(event.target.result);
    };

    reader.readAsDataURL(file);
};

return (
    <div>
        <input
            accept="image/*"
            id="contained-button-file"
            multiple
            type="file"
            style={{ display: 'none' }}
            onChange={handleChangeImage}
        />

        <img id="preview" src={imagePreview} />
    </div>
);

SimpleDialog.test.js

it('should change image src', () => {
    const event = {
        target: {
            files: [
                {
                    name: 'image.png',
                    size: 50000,
                    type: 'image/png'
                }
            ]
        }
    };

    let spy = jest
        .spyOn(FileReader.prototype, 'onload')
        .mockImplementation(() => null);

    wrapper.find('input[type="file"]').simulate('change', event);

    expect(spy).toHaveBeenCalled();

    expect(wrapper.find('#preview').prop('src')).not.toBeNull();
});

运行测试时出现错误 TypeError: 非法调用

谁能帮我处理这个单元测试?我只想模拟当图像的 src 是否具有值时进行更改。


我建议您使用URL.createObjectURL,但是在不再需要它时,请不要忘记撤销它。 - Endless
2个回答

6
错误的原因是将 onload 定义为属性描述符并将其分配给 FileReader.prototype,这是由 spyOn 执行的,但不受支持。
没有理由模拟 onload,因为它在被测试的代码中被赋值并需要测试。
直接的方法是不要修补 JSDOM 的 FileReader 实现,而是完全替换它:
jest.spyOn(global, 'FileReader').mockImplementation(function () {
    this.readAsDataURL = jest.fn();
});

wrapper.find('input[type="file"]').simulate('change', event);


let reader = FileReader.mock.instances[0];
expect(reader.readAsDataURL).toHaveBeenCalledWith(...);
expect(reader.onload).toBe(expect.any(Function));

expect(wrapper.find('#preview').prop('src')).toBeNull();

reader.onload({ target: { result: 'foo' } });

expect(wrapper.find('#preview').prop('src')).toBe('foo');

谢谢@Estus Flask。我对onload事件有点困惑。所以这里的所有方法和事件都是模拟的吗?你能给我一个参考吗? - aldrin27
1
onload只是load事件的监听器,该事件由FileReader触发,https://developer.mozilla.org/en-US/docs/Web/API/FileReader/onload。在这里,测试了组件是否设置了监听器并且它产生了必要的副作用。 - Estus Flask

0

遇到了同样的问题,需要检查当FileReader.onload被调用时应该会发生的某些副作用,所以我最终在触发事件后设置了短暂的延迟(我使用的是enzyme + jest)。在这里设置超时可能不是最好的解决方案,但这是唯一有效的方法。

const pauseFor = milliseconds => new Promise(resolve => setTimeout(resolve, milliseconds));
...
wrapper.find('.upload-box').simulate('drop', someMockedDropEvent);
// set pause was the only way to make reader.onload to fire
await pauseFor(100);
expect(something).toEqual(something)

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