테스팅 도구

Importing

import ReactTestUtils from 'react-dom/test-utils'; // ES6
var ReactTestUtils = require('react-dom/test-utils'); // npm과 ES5

개요

ReactTestUtils는 여러분이 선택한 테스팅 프레임워크에서 테스트를 쉽게 진행할 수 있도록 해 줍니다. Facebook에서는 Jest를 이용해 더욱 쉽게 JavaScript 테스트를 하고 있습니다. Jest 웹사이트의 React 자습서 문서를 통해 Jest를 시작하는 방법에 대해서 알아보세요.

주의

Facebook에서는 React Testing Library 사용을 권장합니다. 이 라이브러리는 사용자가 컴포넌트를 사용하는 것처럼 테스트를 작성할 수 있도록 설계되었습니다.

대안으로는 Airbnb에서 출시한 테스팅 도구인 Enzyme이 있습니다. Enzyme은 React 컴포넌트의 출력을 쉽게 검증하고 조작하고 탐색할 수 있게 해줍니다.

참조사항

act()

컴포넌트의 진단을 준비하기 위해서는 컴포넌트를 렌더링하고 갱신해주는 코드를 act()를 호출한 것의 안에 넣어줘야 합니다. 이를 통해 React를 브라우저 내에서 동작하는 것과 비슷한 환경에서 테스트할 수 있습니다.

주의

react-test-renderer를 사용한다면, 똑같이 작동하는 act export가 제공됩니다.

예를 들어, 다음과 같은 Counter 컴포넌트가 있다고 해봅시다.

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: 0};
    this.handleClick = this.handleClick.bind(this);
  }
  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }
  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }
  handleClick() {
    this.setState(state => ({
      count: state.count + 1,
    }));
  }
  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={this.handleClick}>
          Click me
        </button>
      </div>
    );
  }
}

이런 방식으로 테스트 할 수 있습니다.

import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import Counter from './Counter';

let container;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});

afterEach(() => {
  document.body.removeChild(container);
  container = null;
});

it('can render and update a counter', () => {
  // 첫 render와 componentDidMount를 테스트
  act(() => {
    ReactDOM.render(<Counter />, container);
  });
  const button = container.querySelector('button');
  const label = container.querySelector('p');
  expect(label.textContent).toBe('You clicked 0 times');
  expect(document.title).toBe('You clicked 0 times');

  // 두 번째 render와 componentDidUpdate를 테스트
  act(() => {
    button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
  });
  expect(label.textContent).toBe('You clicked 1 times');
  expect(document.title).toBe('You clicked 1 times');
});
  • DOM 이벤트 발행은 DOM 컨테이너가 document 객체에 추가되었을 때만 작동한다는 점을 잊지마세요. 불필요하게 반복 되는 코드를 줄이기 위해서 react-testing-library와 같은 라이브러리를 사용할 수 있습니다.

  • 테스트 방법 문서에 act()의 동작 방식에 대한 자세한 내용이 예시와 사용법과 함께 포함되어 있습니다.


mockComponent()

mockComponent(
  componentClass,
  [mockTagName]
)

모의 컴포넌트 모듈을 이 메서드에 넘겨 유용한 메서드들을 붙여 증강해 더미 리액트 컴포넌트로 사용할 수 있습니다. 보통의 경우처럼 렌더링 하지 않고 그 대신 컴포넌트는 간단하게 <div> 태그가 됩니다. mockTagName값을 넘겨준다면 <div>대신 다른 태그로 만들어 줄 수 있습니다.

주의

mockComponent()는 더 이상 쓰이지 않는 API입니다. jest.mock() 사용을 추천합니다.


isElement()

isElement(element)

element가 React의 element라면 true를 반환합니다.


isElementOfType()

isElementOfType(
  element,
  componentClass
)

elementcomponentClass 타입의 React element라면 true를 반환합니다.


isDOMComponent()

isDOMComponent(instance)

instance<div><span>같은 DOM 컴포넌트라면 true를 반환합니다.


isCompositeComponent()

isCompositeComponent(instance)

instance가 클래스나 함수 같이 사용자가 정의한 컴포넌트라면 true를 반환합니다.


isCompositeComponentWithType()

isCompositeComponentWithType(
  instance,
  componentClass
)

instancecomponentClass 타입을 가진 컴포넌트라면 true를 반환합니다.


findAllInRenderedTree()

findAllInRenderedTree(
  tree,
  test
)

tree의 모든 컴포넌트를 탐색하여 test(component)true일 때 모든 컴포넌트를 축적합니다. 이 함수는 그 자체만으로는 유용하지 않지만, 다른 테스트 도구의 기반이 됩니다.


scryRenderedDOMComponentsWithClass()

scryRenderedDOMComponentsWithClass(
  tree,
  className
)

렌더링 된 트리에서 조건 className에 만족하는 class명을 가지고 있는 DOM 컴포넌트의 DOM 엘리먼트를 모두 검색합니다.


findRenderedDOMComponentWithClass()

findRenderedDOMComponentWithClass(
  tree,
  className
)

scryRenderedDOMComponentsWithClass()와 기능이 유사하나 결과값이 하나라고 가정하고 그 결과값만을 반환합니다. 두 개 이상의 결과값이 있다면 예외를 반환합니다.


scryRenderedDOMComponentsWithTag()

scryRenderedDOMComponentsWithTag(
  tree,
  tagName
)

렌더링 된 트리 내에서 조건 tagName에 만족하는 tag명을 가진 DOM 컴포넌트의 DOM 엘리먼트를 모두 검색합니다.


findRenderedDOMComponentWithTag()

findRenderedDOMComponentWithTag(
  tree,
  tagName
)

scryRenderedDOMComponentsWithTag()와 기능이 유사하나 결과값이 하나라고 가정하고 그 결과값만을 반환합니다. 두 개 이상의 결과값이 있다면 예외를 뱉습니다.


scryRenderedComponentsWithType()

scryRenderedComponentsWithType(
  tree,
  componentClass
)

componentClass 타입을 가진 모든 인스턴스를 검색합니다.


findRenderedComponentWithType()

findRenderedComponentWithType(
  tree,
  componentClass
)

scryRenderedComponentsWithType()와 기능이 유사하나 결과값이 하나라고 가정하고 그 결과값만을 반환합니다. 두 개 이상의 결과값이 있다면 예외를 뱉습니다.


renderIntoDocument()

renderIntoDocument(element)

React 엘리먼트를 document내의 떨어져 있는 DOM 노드에 렌더링합니다. 이 함수를 쓰려면 DOM이 필요합니다. 이 함수는 다음 코드와 같은 기능을 합니다.

const domContainer = document.createElement('div');
ReactDOM.render(element, domContainer);

주의

window, window.documentwindow.document.createElementReact가져와서 사용하기 전에도 전역적으로 사용할 수 있습니다. 만약 그렇지 않다면 React는 DOM에 접근할 수 없다고 간주할 것이며 setState와 같은 메서드들이 작동하지 않을 것 입니다.


다른 테스팅 도구들

Simulate

Simulate.{eventName}(
  element,
  [eventData]
)

이벤트 데이터인 eventData를 옵션으로 준 DOM 노드에 붙이는 이벤트를 시뮬레이팅합니다.

SimulateReact가 이해하는 모든 이벤트를 위한 메서드를 가집니다.

엘리먼트 클릭

// <button ref={(node) => this.button = node}>...</button>
const node = this.button;
ReactTestUtils.Simulate.click(node);

입력 필드의 값을 바꾼 뒤 ENTER키 누르기

// <input ref={(node) => this.textInput = node} />
const node = this.textInput;
node.value = 'giraffe';
ReactTestUtils.Simulate.change(node);
ReactTestUtils.Simulate.keyDown(node, {key: "Enter", keyCode: 13, which: 13});

주의

컴포넌트 내에서 사용하고 있는 keyCode, which와 같은 이벤트 프로퍼티는 별도로 제공해주어야 합니다. React에서는 이러한 이벤트 프로퍼티를 자동으로 만들어 주지 않습니다.