React学习之扩展Test工具(二十九)

前端之家收集整理的这篇文章主要介绍了React学习之扩展Test工具(二十九)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

套路走起

import ReactTestUtils from 'react-addons-test-utils' // ES6
var ReactTestUtils = require('react-addons-test-utils') // ES5 with npm
var ReactTestUtils = React.addons.TestUtils; // ES5 with react-with-addons.js

1.概述

ReactTestUtils对来说,是一个测试react组件的很好框架,在facebook中我们使用Jest来进行javascript的测试,这里我们将讲述怎么通过React去测试。

注意

Airbnb曾经开发出一款基于React的测试工具Enzyme,这个测试工具用来测试React非常不错,如果你决定不用React自身提供的测试工具,而是想用其他的,这款测试工具是值得试一试的。

React自身测试工具设计的函数

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

2.浅呈现(针对虚拟DOM的测试方式)

浅呈现可以让你的组件只渲染第一层,不渲染所有子组件,如果在浅呈现时进行断言render方法,那么就会直接返回,不需要去管子组件的行为,因为子组件不会进行实例化和呈现,可以说子组件在浅呈现断言时就相当于没有子组件。

下面是浅呈现的实现方式

createRenderer() shallowRenderer.render() shallowRenderer.getRenderOutput()

createRenderer()

这个函数会在你的测试中创建一个浅呈现,你可以用它来代替平时render渲染到视图中的操作,然后进行测试,从而可以提取出组件的输出

shallowRenderer.render()

这个函数类似于ReactDOM.render(),但是通过它并不会加入到DOM中,而仅仅只是渲染一层的深度,也就是说不会处理子组件,这样我们可以通过后续的shallowRenderer.getRenderOutput()函数来分离出子组件

shallowRenderer.getRenderOutput()

shallowRenderer.render()或者createRenderer() 创建的render调用后,你可以使用这个函数,进行浅呈现的输出

到这里你或许会觉得,这都写的什么鬼,不用着急,请看例子

这是一个要呈现的函数式组件

function MyComponent() {
  return (
    <div>
      <span className="heading">Title</span>
      <Subcomponent foo="bar" />
    </div>
  );
}

断言测试

const renderer = ReactTestUtils.createRenderer();
renderer.render(<MyComponent />);
const result = renderer.getRenderOutput();

//下面代码请在nodejs环境下测试
expect(result.type).toBe('div');
expect(result.props.children).toEqual([
  <span className="heading">Title</span>,<Subcomponent foo="bar" />
]);

当然浅展现测试存在一定程度上的局限性。

3.函数详解

Simulate对象

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

Simulate(模拟事件)在DOM节点上派发,附带可选的eventData事件数据。这可能是在ReactTestUtils中最有用的工具。

Simulate为每一个事件都提供了一个方法用来模拟该事件。

点击元素

// <button ref="button">...</button>
const node = this.refs.button;
ReactTestUtils.Simulate.click(node);

改变元素和按键事件

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

其他事件react官网上也有

该注意的是,因为你是模拟事件,所以你要提供所有事件产生的数据。

renderIntoDocument()

renderIntoDocument(element)

把一个组件渲染成一个在文档中分离的DOM节点(即将组件渲染成一个DOM节点但是不将这个节点插入到视图中),返回一个DOM节点。

注意

方法要求存在一个真实的DOM环境,否则会报错。因此,测试用例之中,DOM环境(即window,documentnavigator 对象)必须是存在的。

(个人测试并没有什么卵用,可能有用,没测试出来)

mockComponent()

mockComponent( componentClass,[mockTagName] )

传递一个虚拟的组件模块给这个方法,给这个组件扩充一些有用的方法,让组件能够被当成一个React组件的仿制品来使用。这个组件将会变成一个简单的<div>(或者是其它标签,如果mockTagName提供了的话),包含任何提供的子节点,而不是像往常一样渲染出来。

isElement()

isElement(element)

如果element是一个任意React元素,则返回true。

isElementOfType()

isElementOfType( element,componentClass )

如果element是一个类型为componentClassReact元素,则返回true

isDOMComponent()

isDOMComponent(instance)
//源码
isDOMComponent: function (inst) {
    return !!(inst && inst.nodeType === 1 && inst.tagName);
}

如果是一个DOM组件(例如<div>或者<span>),则返回true

isCompositeComponent()

isCompositeComponent(instance)
//源码
isCompositeComponent: function (inst) {
    if (ReactTestUtils.isDOMComponent(inst)) {
      return false;
    }
    return inst != null && typeof inst.render === 'function' && typeof inst.setState === 'function';
  }

isCompositeComponentWithType()

isCompositeComponentWithType(
  instance,componentClass
)
//源码
isCompositeComponentWithType: function (inst,type) { if (!ReactTestUtils.isCompositeComponent(inst)) { return false; } var internalInstance = ReactInstanceMap.get(inst);
    var constructor = internalInstance._currentElement.type;

    return constructor === type;
  }

如果instancecomponentClass的一个实例则返回true

findAllInRenderedTree()

findAllInRenderedTree(
  tree,test//这是个函数
)
//源码
findAllInRenderedTree: function (inst,test) {
    if (!inst) {
      return [];
    }
    !ReactTestUtils.isCompositeComponent(inst) ? "development" !== 'production' ? invariant(false,'findAllInRenderedTree(...): instance must be a composite component') : _prodInvariant('10') : void 0;
    return findAllInRenderedTreeInternal(ReactInstanceMap.get(inst),test);
  }

遍历tree中所有组件,收集test(component)返回true的所有组件。就这个本身来说不是很有用,但是它可以为其它测试提供原始数据。

scryRenderedDOMComponentsWithClass()

scryRenderedDOMComponentsWithClass(
  tree,className
)
//源码
scryRenderedDOMComponentsWithClass: function (root,classNames) {
    return ReactTestUtils.findAllInRenderedTree(root,function (inst) {
      if (ReactTestUtils.isDOMComponent(inst)) {
        var className = inst.className;
        if (typeof className !== 'string') {
          // SVG,probably.
          className = inst.getAttribute('class') || '';
        }
        var classList = className.split(/\s+/);

        if (!Array.isArray(classNames)) {
          !(classNames !== undefined) ? "development" !== 'production' ? invariant(false,'TestUtils.scryRenderedDOMComponentsWithClass expects a className as a second argument.') : _prodInvariant('11') : void 0;
          classNames = classNames.split(/\s+/);
        }
        return classNames.every(function (name) {
          return classList.indexOf(name) !== -1;
        });
      }
      return false;
    });
  }

查找组件的所有实例,这些实例都在渲染后的树中,并且是带有className类名的DOM组件。

findRenderedDOMComponentWithClass()

findRenderedDOMComponentWithClass( tree,className )

类似于scryRenderedDOMComponentsWithClass(),但是它只返回一个结果,如果有其它满足条件的,则会抛出异常。

scryRenderedDOMComponentsWithTag()

scryRenderedDOMComponentsWithTag( tree,tagName )

在渲染后的树中找出所有组件实例,并且是标签名字符合tagNameDOM组件。

findRenderedDOMComponentWithTag

findRenderedDOMComponentWithTag( tree,tagName )

类似于scryRenderedDOMComponentsWithTag(),但是它只返回一个结果,如果有其它满足条件的,则会抛出异常。

scryRenderedComponentsWithType

scryRenderedComponentsWithType( tree,componentClass )

找出所有组件实例,这些组件的类型为componentClass

findRenderedComponentWithType()

findRenderedComponentWithType( tree,componentClass )

类似于scryRenderedComponentsWithType(),但是它只返回一个结果,如果有其它满足条件的,则会抛出异常。

这里需要注意的是,我把大部分函数的实现源码都展现出来了,这里大家需要注意一个问题,我们用的组件类和最终形成的组件是不同的,也就是说的React组件元素并不是我们的组件的实例.

如下:

class Tmq extends React.Component{
    constructor(props){
        super(props);
    }
    render(){
        return (<MyComponent/>);
    }
}
console.log(TestUtils.isCompositeComponentWithType(<Tmq/>,Tmq));
//返回false
/*通过源码我们也知道isCompositeComponentWithType的判断方式,而Tmq这个对象根本没有render和setState函数,所以很明显<Tmq/>和组件类根本是两个玩意,<Tmq/>是通过React.createElement创建的,两者不能混为一谈*/

下面的代码就会返回true
class Tmq extends React.Component{
    constructor(props){
        super(props);
    }
    render(){
        return (<MyComponent/>);
    }
}
class Tmq extends React.Component{
    constructor(props){
        super(props);
    }
    render(){
        console.log(TestUtils.isCompositeComponent(this,Tmq))
        return (<MyComponent/>);
    }
}
ReactDOM.render(
    <Tmq/>,document.getElementById('example')
    );
/* 由此我们可以推断出一些东西出来: 首先,<Tmq />并不是直接用组件类实例化出来的,它经过了React.createElement来处理。 然后调用ReactDOM.render()函数时,才会调用render进行渲染所以,组件类的实例化部分是进行在ReactDOM.render中的。 */

下一篇将讲ReactAnimation工具

猜你在找的React相关文章