如何使用 Jest/Enzyme 在 React 中测试文件类型输入的更改处理程序? [英] How can I test a change handler for a file-type input in React using Jest/Enzyme?

查看:22
本文介绍了如何使用 Jest/Enzyme 在 React 中测试文件类型输入的更改处理程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想测试我的 React 组件是否可以使用 FileReader 导入用户选择的文件的内容> 元素.我下面的代码显示了一个测试失败的工作组件.

I want to test whether my React component can use FileReader to import the contents of a user-selected file from an <input type="file"/> element. My code below shows a working component with a broken test.

在我的测试中,我尝试使用 blob 作为文件的替代品,因为 blob 也可以被 FileReader读取".这是一种有效的方法吗?我还怀疑部分问题在于 reader.onload 是异步的,我的测试需要考虑到这一点.我需要在某个地方做出承诺吗?或者,我可能需要使用 jest.fn() 模拟 FileReader 吗?

In my test I'm attempting to use a blob as a substitute for the file because blobs can also be "read" by FileReader. Is that a valid approach? I also suspect that part of the issue is that reader.onload is asynchronous and that my test needs to take this into consideration. Do I need a promise somewhere? Alternatively, do I perhaps need to mock FileReader using jest.fn()?

我真的更愿意只使用标准的 React 堆栈.特别是我想使用 Jest 和 Enzyme 而不必使用 Jasmine 或 Sinon 等.但是如果你知道某些事情不能用 Jest/Enzyme 完成但可以 以另一种方式完成,这可能也有帮助.

I would really prefer to only use the standard React stack. In particular I want to use Jest and Enzyme and not have to use, say, Jasmine or Sinon, etc. However if you know something can't be done with Jest/Enzyme but can be done another way, that might also be helpful.

MyComponent.js:

import React from 'react';
class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {fileContents: ''};
        this.changeHandler = this.changeHandler.bind(this);
    }
    changeHandler(evt) {
        const reader = new FileReader();
        reader.onload = () => {
            this.setState({fileContents: reader.result});
            console.log('file contents:', this.state.fileContents);
        };
        reader.readAsText(evt.target.files[0]);
    }
    render() {
        return <input type="file" onChange={this.changeHandler}/>;
    }
}
export default MyComponent;

MyComponent.test.js:

import React from 'react'; import {shallow} from 'enzyme'; import MyComponent from './MyComponent';
it('should test handler', () => {
    const blob = new Blob(['foo'], {type : 'text/plain'});
    shallow(<MyComponent/>).find('input')
        .simulate('change', { target: { files: [ blob ] } });
    expect(this.state('fileContents')).toBe('foo');
});

推荐答案

这个答案展示了如何使用 jest 访问代码的所有不同部分.然而,这并不一定意味着应该以这种方式测试所有这些部分.

This answers shows how to access all of the different parts of the code using jest. However, it doesn't necessarily mean that one should test all of these parts this way.

被测代码与问题中的代码基本相同,只是我将 addEventListener('load', ... 替换为 onload = ...>,我已经删除了 console.log 行:

The code-under-test is essentially the same as in the question except that I have substituted addEventListener('load', ... for onload = ..., and I have removed the console.log line:

MyComponent.js:

import React from 'react';
class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {fileContents: ''};
        this.changeHandler = this.changeHandler.bind(this);
    }
    changeHandler(evt) {
        const reader = new FileReader();
        reader.addEventListener('load', () => {
            this.setState({fileContents: reader.result});
        });
        reader.readAsText(evt.target.files[0]);
    }
    render() {
        return <input type="file" onChange={this.changeHandler}/>;
    }
}
export default MyComponent;

我相信我已经设法测试了被测代码中的几乎所有内容(评论中指出并在下面进一步讨论的一个例外),如下所示:

I believe I've managed to test just about everything in the code-under-test (with the one exception noted in the comments and discussed further below) with the following:

MyComponent.test.js:

import React from 'react';
import {mount} from 'enzyme';
import MyComponent from './temp01';

it('should test handler', () => {
    const componentWrapper   = mount(<MyComponent/>);
    const component          = componentWrapper.get(0);
    // should the line above use `componentWrapper.instance()` instead?
    const fileContents       = 'file contents';
    const expectedFinalState = {fileContents: fileContents};
    const file               = new Blob([fileContents], {type : 'text/plain'});
    const readAsText         = jest.fn();
    const addEventListener   = jest.fn((_, evtHandler) => { evtHandler(); });
        // WARNING: But read the comment by Drenai for a potentially serious
        // problem with the above test of `addEventListener`.
    const dummyFileReader    = {addEventListener, readAsText, result: fileContents};
    window.FileReader        = jest.fn(() => dummyFileReader);

    spyOn(component, 'setState').and.callThrough();
    // spyOn(component, 'changeHandler').and.callThrough(); // not yet working

    componentWrapper.find('input').simulate('change', {target: {files: [file]}});

    expect(FileReader        ).toHaveBeenCalled    (                             );
    expect(addEventListener  ).toHaveBeenCalledWith('load', jasmine.any(Function));
    expect(readAsText        ).toHaveBeenCalledWith(file                         );
    expect(component.setState).toHaveBeenCalledWith(expectedFinalState           );
    expect(component.state   ).toEqual             (expectedFinalState           );
    // expect(component.changeHandler).toHaveBeenCalled(); // not yet working
});

我还没有明确测试的一件事是是否调用了 changeHandler.这似乎应该很容易,但无论出于何种原因,我仍然无法理解.它显然被调用,因为其他模拟函数被确认已被调用,但我还没有能够检查它本身是否被调用,要么使用jest.fn() 甚至是 Jasmine 的 spyOn.我已经在 SO 上询问了另一个问题,以尝试解决这个剩余的问题.

The one thing I haven't explicitly tested yet is whether or not changeHandler was called. This seems like it should be easy but for whatever reason it is still eluding me. It clearly has been called, as other mocked functions within it are confirmed to have been called but I haven't yet been able to check whether it itself was called, either using jest.fn() or even Jasmine's spyOn. I have asked this other question on SO to try to address this remaining problem.

这篇关于如何使用 Jest/Enzyme 在 React 中测试文件类型输入的更改处理程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆