我想测试我的React组件是否可以用来FileReader
从元素中导入用户选择的文件的内容.我的下面的代码显示了一个测试中断的工作组件.
在我的测试中,我试图使用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 ; } } 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().find('input') .simulate('change', { target: { files: [ blob ] } }); expect(this.state('fileContents')).toBe('foo'); });
Andrew Wille.. 19
这个答案显示了如何使用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 ; } } 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(); 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()
甚至茉莉花的spyOn
.我已经在SO上提出了另一个问题,试图解决这个问题.
这个答案显示了如何使用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 ; } } 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(); 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()
甚至茉莉花的spyOn
.我已经在SO上提出了另一个问题,试图解决这个问题.