跳转至

React测试

React组件至少有两个方面需要测试。

  • 给定属性(property)和状态(state),会渲染出什么结果?
  • 对于渲染的结果,进行某种输入(用户的互动),有没有可能从状态A转化成状态B?

第一类测试可以称为测试结构,第二类测试可以称之为测试行为。

推荐的工具集

  • Shallow rendering
  • Regular test utilities and jsdom when needed
  • mocha + jsdom + expect + expect-jsx + babel-plugin-rewire

测试结构:真实DOM测试

mocha-jsdom模块让你可以在describe方法内部或者全局环境,使用jsdom()方法,将Node.js环境模拟成一个浏览器环境,支持所有DOM和浏览器的API,让windowdocumenthistory等等变量在全局环境下可用。

jsdom的参数useEach表示,这些全局变量绑定Mocha的beforeEach/afterEach方法,而不是before/after方法,默认等于false

react-addons-test-utils模块是React官方提供的测试工具库。它的renderIntoDocument方法,接受一个React组件作为参数,将其渲染成Document对象上面的一个真实DOM结构,但是不会将其插入文档。这个方法执行的前提是windowwindow.documentwindow.document.createElement必须在调用React之前,就在全局范围之中可以拿到。否则,React会认为无法拿到DOM,然后诸如setState这样的方法就不可用。

react-dom模块的findDOMNode方法,它接受一个React组件作为参数。如果该组件已经加到了DOM,那么该方法返回对应的浏览器之中的真实DOM。这个方法主要用于从真实方法取值,比如表单项的值。大多数情况,从组件的ref属性可以拿到真实DOM,不一定要使用findDOMNode方法。如果render方法返回nullfalsefindDOMNode方法也会返回null

findDOMNode()只对已经加载的组件有效(即已经放入DOM),否则会报错。

import React from 'react';
import {findDOMNode} from 'react-dom';
import {renderIntoDocument} from 'react-addons-test-utils';
import jsdom from 'mocha-jsdom';
import expect from 'expect';

class Label extends React.Component {
  render() {
    return <span>Hello {this.props.name}</span>;
  }
}

class Button extends React.Component {
  render() {
    return <div><Label name={this.props.name} /></div>;
  }
}

describe('Real dom test', () => {
  jsdom({useEach: true});

  it('works', () => {
    let component = renderIntoDocument(<Button name="John" />);
    let DOMNode = findDOMNode(component);
    expect(
      DOMNode.querySelector('span')
        .textContent
    ).toEqual('Hello John');
  });
});

例二。

import React from 'react';
import {
   renderIntoDocument,
  findRenderedDOMComponentWithTag
} from 'react-addons-test-utils';
import jsdom from 'mocha-jsdom';
import expect from 'expect';

class Label extends React.Component {
  render() {
    return <span>Hello {this.props.name}</span>;
  }
}

class Button extends React.Component {
  render() {
    return <div><Label name={this.props.name} /></div>;
  }
}

describe('Real Test Utilities', () => {
  jsdom({useEach: true});

  it('works', () => {
    let component = renderIntoDocument(<Button name="John" />);
    expect(
      findRenderedDOMComponentWithTag(component, 'span')
        .textContent
    ).toEqual('Hello John');
  });
});

例三。

import React from 'react';
import expect from 'expect';
import {createRenderer} from 'react-addons-test-utils';

class Label extends React.Component {
  render() {
    return <span>Hello {this.props.name}</span>;
  }
}

class Button extends React.Component {
  render() {
    return <div><Label name={this.props.name} /></div>;
  }
}

describe('Shallow rendering', () => {
  it('works', () => {
    let renderer = createRenderer();
    renderer.render(<Button name="John" />);
    let actualElement = renderer.getRenderOutput();
    let expectedElement = <div><Label name="John" /></div>;
    expect(actualElement).toEqual(expectedElement);
  });
});

测试行为

import React from 'react';
import expect from 'expect';
import {createRenderer} from 'react-addons-test-utils';

class Label extends React.Component {
  render() {
    return <span>Hello {this.props.name}</span>;
  }
}

class Button extends React.Component {
  render() {
    return <div onClick={this.props.click}><Label name={this.props.name} /></div>;
  }
}

describe('Shallow rendering on* handlers', () => {
  it('works', () => {
    let renderer = createRenderer();
    let hasClicked = false;
    let click = () => hasClicked = true;
    renderer.render(<Button name="John" click={click} />);
    renderer.getRenderOutput().props.onClick();
    expect(hasClicked).toBe(true);
  });
});

参考链接