• Test Utilities
    • 概览
    • 参考
      • act()
      • mockComponent()
      • isElement()
      • isElementOfType()
      • isDOMComponent()
      • isCompositeComponent()
      • isCompositeComponentWithType()
      • findAllInRenderedTree()
      • scryRenderedDOMComponentsWithClass()
      • findRenderedDOMComponentWithClass()
      • scryRenderedDOMComponentsWithTag()
      • findRenderedDOMComponentWithTag()
      • scryRenderedComponentsWithType()
      • findRenderedComponentWithType()
      • renderIntoDocument()
    • 其他工具方法
      • Simulate

    Test Utilities

    如何引入

    1. import ReactTestUtils from 'react-dom/test-utils'; // ES6
    2. var ReactTestUtils = require('react-dom/test-utils'); // ES5 使用 npm 的方式

    概览

    ReactTestUtils 可搭配你所选的测试框架,轻松实现 React 组件测试。在 Facebook 内部,我们使用 Jest 来轻松实现 JavaScript 测试。你可以从 Jest 官网的 React 教程中了解如何开始使用它。

    注意:

    我们推荐使用 React Testing Library,它使得针对组件编写测试用例就像终端用户在使用它一样方便。

    另外,Airbnb 发布了一款叫作 Enzyme 的测试工具,通过它能够轻松对 React 组件的输出进行断言、操控和遍历。

    • act()
    • mockComponent()
    • isElement()
    • isElementOfType()
    • isDOMComponent()
    • isCompositeComponent()
    • isCompositeComponentWithType()
    • findAllInRenderedTree()
    • scryRenderedDOMComponentsWithClass()
    • findRenderedDOMComponentWithClass()
    • scryRenderedDOMComponentsWithTag()
    • findRenderedDOMComponentWithTag()
    • scryRenderedComponentsWithType()
    • findRenderedComponentWithType()
    • renderIntoDocument()
    • Simulate

    参考

    act()

    为断言准备一个组件,包裹要渲染的代码并在调用 act() 时执行更新。这会使得测试更接近 React 在浏览器中的工作方式。

    注意

    如果你使用了 react-test-renderer,它也提供了与 act 行为相同的函数。

    例如,假设我们有个 Counter 组件:

    1. class Counter extends React.Component {
    2. constructor(props) {
    3. super(props);
    4. this.state = {count: 0};
    5. this.handleClick = this.handleClick.bind(this);
    6. }
    7. componentDidMount() {
    8. document.title = `You clicked ${this.state.count} times`;
    9. }
    10. componentDidUpdate() {
    11. document.title = `You clicked ${this.state.count} times`;
    12. }
    13. handleClick() {
    14. this.setState(state => ({
    15. count: state.count + 1,
    16. }));
    17. }
    18. render() {
    19. return (
    20. <div>
    21. <p>You clicked {this.state.count} times</p>
    22. <button onClick={this.handleClick}>
    23. Click me
    24. </button>
    25. </div>
    26. );
    27. }
    28. }

    以下是其测试代码:

    1. import React from 'react';
    2. import ReactDOM from 'react-dom';
    3. import { act } from 'react-dom/test-utils';
    4. import Counter from './Counter';
    5. let container;
    6. beforeEach(() => {
    7. container = document.createElement('div');
    8. document.body.appendChild(container);
    9. });
    10. afterEach(() => {
    11. document.body.removeChild(container);
    12. container = null;
    13. });
    14. it('can render and update a counter', () => {
    15. // 首先测试 render 和 componentDidMount
    16. act(() => {
    17. ReactDOM.render(<Counter />, container);
    18. });
    19. const button = container.querySelector('button');
    20. const label = container.querySelector('p');
    21. expect(label.textContent).toBe('You clicked 0 times');
    22. expect(document.title).toBe('You clicked 0 times');
    23. // 再测试 render 和 componentDidUpdate
    24. act(() => {
    25. button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
    26. });
    27. expect(label.textContent).toBe('You clicked 1 times');
    28. expect(document.title).toBe('You clicked 1 times');
    29. });

    千万不要忘记,只有将 DOM 容器添加到 document 时,触发 DOM 事件才生效。你可以使用类似于 React Testing Library 等库来减少样板代码(boilerplate code)。

    • recipes 文档包含了关于 act() 函数的示例、用法及更多详细信息。

    mockComponent()

    1. mockComponent(
    2. componentClass,
    3. [mockTagName]
    4. )

    将模拟组件模块传入这个方法后,React 内部会使用有效的方法填充该模块,使其成为虚拟的 React 组件。与通常的渲染不同,组件将变成一个简单的 <div> (如果提供了 mockTagName 则是其他标签),包含任何提供的子级。

    注意:

    mockComponent() 是一个过时的 API,我们推荐使用 jest.mock() 来代替。


    isElement()

    1. isElement(element)

    element 是任何一种 React 元素时,返回 true


    isElementOfType()

    1. isElementOfType(
    2. element,
    3. componentClass
    4. )

    element 是一种 React 元素,并且它的类型是参数 componentClass 的类型时,返回 true


    isDOMComponent()

    1. isDOMComponent(instance)

    instance 是一个 DOM 组件(比如 <div><span>)时,返回 true


    isCompositeComponent()

    1. isCompositeComponent(instance)

    instance 是一个用户自定义的组件,比如一个类或者一个函数时,返回 true


    isCompositeComponentWithType()

    1. isCompositeComponentWithType(
    2. instance,
    3. componentClass
    4. )

    instance 是一个组件,并且它的类型是参数 componentClass 的类型时,返回 true


    findAllInRenderedTree()

    1. findAllInRenderedTree(
    2. tree,
    3. test
    4. )

    遍历所有在参数 tree 中的组件,记录所有 test(component)true 的组件。单独调用此方法不是很有用,但是它常常被作为底层 API 被其他测试方法使用。


    scryRenderedDOMComponentsWithClass()

    1. scryRenderedDOMComponentsWithClass(
    2. tree,
    3. className
    4. )

    查找渲染树中组件的所有 DOM 元素,这些组件是 css 类名与参数 className 匹配的 DOM 组件。


    findRenderedDOMComponentWithClass()

    1. findRenderedDOMComponentWithClass(
    2. tree,
    3. className
    4. )

    用法与 scryRenderedDOMComponentsWithClass() 保持一致,但期望仅返回一个结果。不符合预期的情况下会抛出异常。


    scryRenderedDOMComponentsWithTag()

    1. scryRenderedDOMComponentsWithTag(
    2. tree,
    3. tagName
    4. )

    查找渲染树中组件的所有的 DOM 元素,这些组件是标记名与参数 tagName 匹配的 DOM 组件。


    findRenderedDOMComponentWithTag()

    1. findRenderedDOMComponentWithTag(
    2. tree,
    3. tagName
    4. )

    用法与 scryRenderedDOMComponentsWithTag() 保持一致,但期望仅返回一个结果。不符合预期的情况下会抛出异常。


    scryRenderedComponentsWithType()

    1. scryRenderedComponentsWithType(
    2. tree,
    3. componentClass
    4. )

    查找组件类型等于 componentClass 组件的所有实例。


    findRenderedComponentWithType()

    1. findRenderedComponentWithType(
    2. tree,
    3. componentClass
    4. )

    用法与 scryRenderedComponentsWithType() 保持一致,但期望仅返回一个结果。不符合预期的情况下会抛出异常。


    renderIntoDocument()

    1. renderIntoDocument(element)

    渲染 React 元素到 document 中的某个单独的 DOM 节点上。这个函数需要一个 DOM 对象。 它实际相当于:

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

    注意:

    你需要在引入 React 之前确保 window 存在,window.documentwindow.document.createElement 能在全局环境中获取到。不然 React 会认为它没有权限去操作 DOM,以及像 setState 这样的方法将不可用。


    其他工具方法

    Simulate

    1. Simulate.{eventName}(
    2. element,
    3. [eventData]
    4. )

    使用可选的 eventData 事件数据来模拟在 DOM 节点上触发事件。

    React 所支持的所有事件 在 Simulate 中都有对应的方法。

    点击元素

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

    修改一个 input 输入框的值,然后按回车键。

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

    注意:

    你必须提供一切需要在组件中用到的事件属性(比如:keyCode、which 等等),因为 React 没有为你创建这些属性。