Test Utilities

如何 Import

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

概觀

ReactTestUtils 使你可以輕鬆在你選擇的測試框架中測試 React component。在 Facebook,我們使用 Jest 以方便地進行 JavaScript 測試。你可以從 Jest 網站的 React 教學學習如何使用 Jest。

注意:

我們推薦使用 React Testing Library,它促使你寫出的測試能像使用者一樣地使用 component。

此外,Airbnb 推出了名為 Enzyme 的測試工具,讓你能輕易 assert、操作及遍歷 React component 的輸出。

參考資料

act()

為了準備讓 component 進行 assert,將 render component 及執行更新的程式碼放在 act() 中。這讓你的測試更貼近 React 在瀏覽器中的運作方式。

注意

如果你使用 react-test-renderer,它也提供行為相同的 act function。

舉例來說,假設我們有個 Counter component:

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', () => {
  // Test first render and 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');

  // Test second render and 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 container 已加到 document 裡面時,才可以 dispatch DOM event。你可以使用如 react-testing-library 的 helper 來減少 boilerplate 程式碼。

  • recipes 說明文件內包含了 act() 的詳細資訊,包含範例以及用法。


mockComponent()

mockComponent(
  componentClass,
  [mockTagName]
)

傳遞一個被 mock 的 component module 到這個方法後,它會增加有用的方法,讓它能做為虛擬的 React component。component 不會像平常一樣 render,它會變成一個簡單的 <div>(或其他標籤,如果有提供 mockTagName),包含任何提供的 children。

注意:

mockComponent() 是 legacy API。我們建議以 jest.mock() 作為替代。


isElement()

isElement(element)

如果 element 是 React element 的話就回傳 true


isElementOfType()

isElementOfType(
  element,
  componentClass
)

如果 element 是 type 為 componentClass 的 React element 就回傳 true


isDOMComponent()

isDOMComponent(instance)

如果 instance 是 DOM component(如 <div><span>)就回傳 true


isCompositeComponent()

isCompositeComponent(instance)

如果 instance 是使用者定義的 component,例如 class 或 function,就回傳 true


isCompositeComponentWithType()

isCompositeComponentWithType(
  instance,
  componentClass
)

如果 instance 是 type 為 componentClass 的 component 就回傳 true


findAllInRenderedTree()

findAllInRenderedTree(
  tree,
  test
)

遍歷 tree 中的所有 component,並收集 test(component)true 的所有 component。這個方法本身不是那麼好用,但是它被其他測試工具做為基礎使用。


scryRenderedDOMComponentsWithClass()

scryRenderedDOMComponentsWithClass(
  tree,
  className
)

在已經被 render 的 tree 中尋找所有 DOM element,回傳 class 名稱符合 className 的 DOM component。


findRenderedDOMComponentWithClass()

findRenderedDOMComponentWithClass(
  tree,
  className
)

scryRenderedDOMComponentsWithClass() 相似,不過預期只有一個結果。如果符合預期則回傳那個結果,否則拋出例外。


scryRenderedDOMComponentsWithTag()

scryRenderedDOMComponentsWithTag(
  tree,
  tagName
)

在已經被 render 的 tree 中尋找所有 DOM element,回傳 tag 名稱符合 tagName 的 DOM component。


findRenderedDOMComponentWithTag()

findRenderedDOMComponentWithTag(
  tree,
  tagName
)

scryRenderedDOMComponentsWithTag() 相似,不過預期只有一個結果。如果符合預期則回傳那個結果,否則拋出例外。


scryRenderedComponentsWithType()

scryRenderedComponentsWithType(
  tree,
  componentClass
)

尋找所有 component type 與 componentClass 相同的 instance。


findRenderedComponentWithType()

findRenderedComponentWithType(
  tree,
  componentClass
)

scryRenderedComponentsWithType() 相似,不過預期只有一個結果。如果符合預期則回傳那個結果,否則拋出例外。


renderIntoDocument()

renderIntoDocument(element)

Render React element 到 document 中獨立的 DOM node 裡。這個 function 需要 DOM。它等效於:

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

注意:

在 import React ,你需要讓 windowwindow.documentwindow.document.createElement 在全域可以使用。否則 React 會認為它無法存取 DOM,像 setState 之類的方法也將無法運作。


其他工具

Simulate

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

在 DOM node 上用可選的 eventData 事件資料模擬 event dispatch。

每一個 React 支援的事件Simulate 都有對應的方法。

點擊 element

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

注意:

你需要提供所有在你的 component 中有使用的事件屬性(如 keyCode、which 等等),因為 React 不會為你建立這些東西。