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()mockComponent()isElement()isElementOfType()isDOMComponent()isCompositeComponent()isCompositeComponentWithType()findAllInRenderedTree()scryRenderedDOMComponentsWithClass()findRenderedDOMComponentWithClass()scryRenderedDOMComponentsWithTag()findRenderedDOMComponentWithTag()scryRenderedComponentsWithType()findRenderedComponentWithType()renderIntoDocument()Simulate
參考資料
act()
為了準備讓 component 進行 assert,將 render component 及執行更新的程式碼放在 act() 中。這讓你的測試更貼近 React 在瀏覽器中的運作方式。
注意
如果你使用
react-test-renderer,它也提供行為相同的actfunction。
舉例來說,假設我們有個 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前,你需要讓window、window.document和window.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 不會為你建立這些東西。