Test Renderer

如何引入

import TestRenderer from 'react-test-renderer'; // ES6
const TestRenderer = require('react-test-renderer'); // ES5 with npm

概览

这个 package 提供了一个 React 渲染器,用于将 React 组件渲染成纯 JavaScript 对象,无需依赖 DOM 或原生移动环境。

这个 package 提供的主要功能是在不依赖浏览器或 jsdom 的情况下,返回某个时间点由 React DOM 或者 React Native 平台渲染出的视图结构(类似与 DOM 树)快照。

示例:

import TestRenderer from 'react-test-renderer';

function Link(props) {
  return <a href={props.page}>{props.children}</a>;
}

const testRenderer = TestRenderer.create(
  <Link page="https://www.facebook.com/">Facebook</Link>
);

console.log(testRenderer.toJSON());
// { type: 'a',
//   props: { href: 'https://www.facebook.com/' },
//   children: [ 'Facebook' ] }

你可以使用 Jest 的快照测试功能来自动保存当前 JSON 树结构到一个文件中,并在测试中检查它是否被修改:了解更多

你也可以通过遍历输出来查找特定节点,并对它们进行断言。

import TestRenderer from 'react-test-renderer';

function MyComponent() {
  return (
    <div>
      <SubComponent foo="bar" />
      <p className="my">Hello</p>
    </div>
  )
}

function SubComponent() {
  return (
    <p className="sub">Sub</p>
  );
}

const testRenderer = TestRenderer.create(<MyComponent />);
const testInstance = testRenderer.root;

expect(testInstance.findByType(SubComponent).props.foo).toBe('bar');
expect(testInstance.findByProps({className: "sub"}).children).toEqual(['Sub']);

TestRenderer

TestRenderer instance

TestInstance

参考

TestRenderer.create()

TestRenderer.create(element, options);

通过传来的 React 元素创建一个 TestRenderer 实例。它并不使用真实的 DOM,但是它依然将组件树完整地渲染到内存,以便于你对它进行断言。此时将返回一个 TestRenderer 实例

TestRenderer.act()

TestRenderer.act(callback);

react-dom/test-utils 中的 act() 相似,TestRender.act 为断言准备一个组件。可以使用 act() 来包装 TestRenderer.createtestRenderer.update

import {create, act} from 'react-test-renderer';
import App from './app.js'; // The component being tested

// 渲染组件
let root; 
act(() => {
  root = create(<App value={1}/>)
});

// 对根元素进行断言
expect(root.toJSON()).toMatchSnapshot();

// 更新 props
act(() => {
  root = root.update(<App value={2}/>);
})

// 对根元素进行断言
expect(root.toJSON()).toMatchSnapshot();

testRenderer.toJSON()

testRenderer.toJSON()

返回一个已渲染的的树对象。该树仅包含特定平台的节点,例如 <div><View> 和它们的 props,但并不包含任何用户编写的组件。这对于快照测试非常方便。

testRenderer.toTree()

testRenderer.toTree()

返回一个已渲染的的树对象。它所表示的内容比 toJSON() 提供的内容要更加详细,并且包含用户编写的组件。除非你要在测试渲染器(test renderer)之上编写自己的断言库,否则你可能并不需要这个方法。

testRenderer.update()

testRenderer.update(element)

使用新的根元素重新渲染内存中的树。它模拟根元素的一次 React 更新。如果新的元素和之前的元素有相同的 type 和 key,该树将会被更新;否则,它将重挂载一个新树。

testRenderer.unmount()

testRenderer.unmount()

卸载内存中的树,会触发相应的生命周期事件。

testRenderer.getInstance()

testRenderer.getInstance()

如果可用的话,返回与根元素相对应的实例。如果根元素是函数定义组件,该方法无效,因为函数定义组件没有实例。

testRenderer.root

testRenderer.root

返回根元素“测试实例”对象,它对于断言树中的特定节点十分有用。你可以利用它来查找其他更深层的“测试实例”。

testInstance.find()

testInstance.find(test)

找到一个 test(testInstance) 返回 true 的后代测试实例。如果不只有一个测试实例匹配,将会报错。

testInstance.findByType()

testInstance.findByType(type)

找到匹配指定 type 的后代测试实例,如果不是只有一个测试实例匹配指定的 type,将会报错。

testInstance.findByProps()

testInstance.findByProps(props)

找到匹配指定 props的后代测试实例,如果不是正好只有一个测试实例匹配指定的 props,将会报错。

testInstance.findAll()

testInstance.findAll(test)

找到所有 test(testInstance) 返回 true 的后代测试实例。

testInstance.findAllByType()

testInstance.findAllByType(type)

找到所有匹配指定 type 的后代测试实例。

testInstance.findAllByProps()

testInstance.findAllByProps(props)

找到所有匹配指定 props 的后代测试实例。

testInstance.instance

testInstance.instance

该测试实例相对应的组件实例。它只能用于类定义组件,因为函数定义组件没有实例。它匹配给定的组件内部的 this 的值。

testInstance.type

testInstance.type

该测试实例相对应的组件的类型。例如,一个 <Button /> 组件有一个 Button 类型。

testInstance.props

testInstance.props

该测试实例相对应的组件的 props。例如,一个 <Button size="small" /> 组件的 props 为 {size: 'small'}

testInstance.parent

testInstance.parent

该测试实例的父测试实例。

testInstance.children

testInstance.children

该测试实例的子测试实例。

想法

你可以把 createNodeMock 函数作为选项(option)传递给 TestRenderer.create,进行自定义 refs 模拟。createNodeMock 接受当前元素作为参数,并且返回一个模拟 ref 对象的。这十分有利于依赖 refs 组件的测试。

import TestRenderer from 'react-test-renderer';

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.input = null;
  }
  componentDidMount() {
    this.input.focus();
  }
  render() {
    return <input type="text" ref={el => this.input = el} />
  }
}

let focused = false;
TestRenderer.create(
  <MyComponent />,
  {
    createNodeMock: (element) => {
      if (element.type === 'input') {
        // 模拟 focus 函数
        return {
          focus: () => {
            focused = true;
          }
        };
      }
      return null;
    }
  }
);
expect(focused).toBe(true);