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

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

套路走起

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

1.概述

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

注意

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

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

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

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

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

下面是浅呈现的实现方式

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

createRenderer()

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

shallowRenderer.render()

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

shallowRenderer.getRenderOutput()

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

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

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

  1. function MyComponent() {
  2. return (
  3. <div>
  4. <span className="heading">Title</span>
  5. <Subcomponent foo="bar" />
  6. </div>
  7. );
  8. }

断言测试

  1. const renderer = ReactTestUtils.createRenderer();
  2. renderer.render(<MyComponent />);
  3. const result = renderer.getRenderOutput();
  4.  
  5. //下面代码请在nodejs环境下测试
  6. expect(result.type).toBe('div');
  7. expect(result.props.children).toEqual([
  8. <span className="heading">Title</span>,<Subcomponent foo="bar" />
  9. ]);

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

3.函数详解

Simulate对象

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

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

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

点击元素

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

改变元素和按键事件

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

其他事件react官网上也有

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

renderIntoDocument()

  1. renderIntoDocument(element)

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

注意

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

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

mockComponent()

  1. mockComponent( componentClass,[mockTagName] )

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

isElement()

  1. isElement(element)

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

isElementOfType()

  1. isElementOfType( element,componentClass )

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

isDOMComponent()

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

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

isCompositeComponent()

  1. isCompositeComponent(instance)
  2. //源码
  3. isCompositeComponent: function (inst) {
  4. if (ReactTestUtils.isDOMComponent(inst)) {
  5. return false;
  6. }
  7. return inst != null && typeof inst.render === 'function' && typeof inst.setState === 'function';
  8. }

isCompositeComponentWithType()

  1. isCompositeComponentWithType(
  2. instance,componentClass
  3. )
  4. //源码
  5. isCompositeComponentWithType: function (inst,type) { if (!ReactTestUtils.isCompositeComponent(inst)) { return false; } var internalInstance = ReactInstanceMap.get(inst);
  6. var constructor = internalInstance._currentElement.type;
  7.  
  8. return constructor === type;
  9. }

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

findAllInRenderedTree()

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

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

scryRenderedDOMComponentsWithClass()

  1. scryRenderedDOMComponentsWithClass(
  2. tree,className
  3. )
  4. //源码
  5. scryRenderedDOMComponentsWithClass: function (root,classNames) {
  6. return ReactTestUtils.findAllInRenderedTree(root,function (inst) {
  7. if (ReactTestUtils.isDOMComponent(inst)) {
  8. var className = inst.className;
  9. if (typeof className !== 'string') {
  10. // SVG,probably.
  11. className = inst.getAttribute('class') || '';
  12. }
  13. var classList = className.split(/\s+/);
  14.  
  15. if (!Array.isArray(classNames)) {
  16. !(classNames !== undefined) ? "development" !== 'production' ? invariant(false,'TestUtils.scryRenderedDOMComponentsWithClass expects a className as a second argument.') : _prodInvariant('11') : void 0;
  17. classNames = classNames.split(/\s+/);
  18. }
  19. return classNames.every(function (name) {
  20. return classList.indexOf(name) !== -1;
  21. });
  22. }
  23. return false;
  24. });
  25. }

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

findRenderedDOMComponentWithClass()

  1. findRenderedDOMComponentWithClass( tree,className )

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

scryRenderedDOMComponentsWithTag()

  1. scryRenderedDOMComponentsWithTag( tree,tagName )

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

findRenderedDOMComponentWithTag

  1. findRenderedDOMComponentWithTag( tree,tagName )

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

scryRenderedComponentsWithType

  1. scryRenderedComponentsWithType( tree,componentClass )

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

findRenderedComponentWithType()

  1. findRenderedComponentWithType( tree,componentClass )

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

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

如下:

  1. class Tmq extends React.Component{
  2. constructor(props){
  3. super(props);
  4. }
  5. render(){
  6. return (<MyComponent/>);
  7. }
  8. }
  9. console.log(TestUtils.isCompositeComponentWithType(<Tmq/>,Tmq));
  10. //返回false
  11. /*通过源码我们也知道isCompositeComponentWithType的判断方式,而Tmq这个对象根本没有render和setState函数,所以很明显<Tmq/>和组件类根本是两个玩意,<Tmq/>是通过React.createElement创建的,两者不能混为一谈*/
  12.  
  13. 下面的代码就会返回true
  14. class Tmq extends React.Component{
  15. constructor(props){
  16. super(props);
  17. }
  18. render(){
  19. return (<MyComponent/>);
  20. }
  21. }
  22. class Tmq extends React.Component{
  23. constructor(props){
  24. super(props);
  25. }
  26. render(){
  27. console.log(TestUtils.isCompositeComponent(this,Tmq))
  28. return (<MyComponent/>);
  29. }
  30. }
  31. ReactDOM.render(
  32. <Tmq/>,document.getElementById('example')
  33. );
  34. /* 由此我们可以推断出一些东西出来: 首先,<Tmq />并不是直接用组件类实例化出来的,它经过了React.createElement来处理。 然后调用ReactDOM.render()函数时,才会调用render进行渲染所以,组件类的实例化部分是进行在ReactDOM.render中的。 */

下一篇将讲ReactAnimation工具

猜你在找的React相关文章