问题 如何使用Jest / Enzyme在React中测试文件类型输入的更改处理程序?


我想测试我的React组件是否可以使用 FileReader 从...导入用户选择的文件的内容 <input type="file"/> 元件。我的下面的代码显示了一个测试中断的工作组件。

在我的测试中,我试图使用blob作为文件的替代品,因为blob也可以“读取” FileReader。这是一种有效的方法吗?我也怀疑问题的一部分是那样的 reader.onload 是异步的,我的测试需要考虑到这一点。我需要某个地方的承诺吗?或者,我是否需要嘲笑 FileReader 运用 jest.fn()

我更愿意只使用标准的React堆栈。特别是我想使用Jest和Enzyme而不必使用Jasmine或Sinon等。但是如果你知道的话 不能 用Jest / Enzyme完成但是 能够 做另一种方式,也可能有所帮助。

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

5104
2018-01-17 17:03


起源

这个讨论 似乎建议使用 addEventListener 绕过React处理事件的策略,因此并不支持酶。 - Andrew Willems
我提到的原因 addEventListener 在第一个评论是因为其他网站建议 addEventListener 可能比测试更可测试 onload。 (链接?)如果我理解正确, 在我的第一条评论中提到的讨论 提出了一些我尚未开始研究的其他测试策略,但它指出这种可能的解决方案超出了React /酶的常规使用范围。然而,它似乎帮助至少一个人测试了一个 mousemove 使用的组件上的事件 addEventListener 但没有提供很多细节。 - Andrew Willems


答案:


这个答案显示 怎么样 使用jest访问代码的所有不同部分。但是,它并不一定意味着一个 应该 以这种方式测试所有这些部件。

除了我已经替换之外,被测代码与问题中的代码基本相同 addEventListener('load', ... 对于 onload = ...,我已经删除了 console.log 线:

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;

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

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(); });
    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上试图解决这个剩余的问题。


13
2018-02-03 02:48