2017-01-17 7 views
5

내 React 구성 요소가 FileReader을 사용하여 <input type="file"/> 요소에서 사용자가 선택한 파일의 내용을 가져올 수 있는지 테스트하고 싶습니다. 아래 코드는 작동하지 않는 구성 요소를 보여줍니다.Jest/Enzyme을 사용하여 React에서 파일 유형 입력에 대한 변경 처리기를 테스트하려면 어떻게해야합니까?

내 테스트에서 blob을 FileReader에 의해 "읽을"수 있기 때문에 파일 대신 blob을 사용하려고합니다. 이것이 유효한 접근 방법입니까? 또한 문제의 일부가 reader.onload이 비동기이며 내 테스트가이를 고려해야한다고 의심됩니다. 어딘가에 약속이 필요합니까? 또는 FileReaderjest.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'); 
}); 
+0

[이 토론] (https://github.com/airbnb/enzyme/issues/426)은 'addEventListener'를 사용하면 React의 이벤트 처리 전략을 무시하므로 실제로 효소가 지원하지 않는다고 제안하는 것으로 보입니다 . –

+0

첫 번째 주석에서'addEventListener'를 언급 한 이유는 다른 사이트에서'addEventListener'가'onload'보다 더 테스트 할 수 있다고 제안했기 때문입니다. (링크?) 내가 올바르게 이해하면 [내 첫 번째 언급에 언급 된 토론] (https://github.com/airbnb/enzyme/issues/426)은 아직 작동하지 않은 다른 테스트 전략을 제안합니다 그러나 가능한 해결책은 React/효소의 규칙적인 사용을 넘어선다고 명시하고있다. 그러나 적어도 하나의 사람이'addEventListener'를 사용했지만 많은 세부 사항을 제공하지 않은 구성 요소에 대해 'mousemove' 이벤트를 테스트하는 데 도움이되는 것으로 보입니다. –

답변

9

이를 사용하여 코드의 다른 부분을 모두 액세스하는 방법을 보여줍니다 응답 농담. 그러나 반드시 그런 의미는 아닙니다. 이 모든 부분을 이렇게 테스트해야합니다.

코드 피 시험은 기본적으로 I는 onload = ...위한 addEventListener('load', ... 치환 된 것을 제외하고는 문제와 동일하고, I는 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() 또는 재 스민의 spyOn 중 하나인지 여부를 확인할 수 없었습니다. 나는이 나머지 문제를 해결하기 위해 SO에 this other question을 요청했습니다.