如何使用浅渲染测试装饰的React组件

时间:2015-07-16 16:28:50

标签: javascript reactjs mocha react-jsx jestjs

我正在关注本教程:http://reactkungfu.com/2015/07/approaches-to-testing-react-components-an-overview/

试图了解"浅层渲染"的工作原理。

我有一个更高阶的组件:

import React from 'react';

function withMUI(ComposedComponent) {
  return class withMUI {
    render() {
      return <ComposedComponent {...this.props}/>;
    }
  };
}

和一个组件:

@withMUI
class PlayerProfile extends React.Component {
  render() {
    const { name, avatar } = this.props;
    return (
      <div className="player-profile">
        <div className='profile-name'>{name}</div>
        <div>
          <Avatar src={avatar}/>
        </div>
      </div>
    );
  }
}

和测试:

describe('PlayerProfile component - testing with shallow rendering', () => {
  beforeEach(function() {
   let {TestUtils} = React.addons;

    this.TestUtils = TestUtils;

    this.renderer = TestUtils.createRenderer();
    this.renderer.render(<PlayerProfile name='user'
                                            avatar='avatar'/>);
  });

  it('renders an Avatar', function() {
    let result = this.renderer.getRenderOutput();
    console.log(result);
    expect(result.type).to.equal(PlayerProfile);
  });
});

result变量包含this.renderer.getRenderOutput()

在教程中,result.type的测试类似于:

expect(result.type).toEqual('div');

在我的情况下,如果我记录result它是:

LOG: Object{type: function PlayerProfile() {..}, .. }

所以我改变了我的测试:

expect(result.type).toEqual(PlayerProfile)

现在它给了我这个错误:

Assertion Error: expected [Function: PlayerProfile] to equal [Function: withMUI]

所以PlayerProfile的类型是高阶函数withMUI

PlayerProfile使用withMUI修饰,使用浅层渲染,只渲染PlayerProfile组件而不是它的子组件。如此浅的渲染对我假设的装饰组件不起作用。

我的问题是:

为什么教程result.type应该是div,但在我的情况下不是。

如何使用浅渲染测试用高阶组件修饰的React组件?

4 个答案:

答案 0 :(得分:19)

你不能。首先让我们稍微去除装饰者:

let PlayerProfile = withMUI(
    class PlayerProfile extends React.Component {
      // ...
    }
);

withMUI返回一个不同的类,因此PlayerProfile类仅存在于withMUI的闭包中。

这是一个简化版本:

var withMUI = function(arg){ return null };
var PlayerProfile = withMUI({functionIWantToTest: ...});

您将该值传递给该函数,它没有将其返回,您没有该值。

解决方案?保留对它的引用。

// no decorator here
class PlayerProfile extends React.Component {
  // ...
}

然后我们可以导出组件的包装和未包装版本:

// this must be after the class is declared, unfortunately
export default withMUI(PlayerProfile);
export let undecorated = PlayerProfile;

使用此组件的普通代码无法更改,但您的测试将使用此代码:

import {undecorated as PlayerProfile} from '../src/PlayerProfile';

另一种方法是将withMUI函数模拟为(x) => x(标识函数)。这可能会导致奇怪的副作用,需要从测试端完成,因此在添加装饰器时,您的测试和源可能会失去同步。

不使用装饰器似乎是安全的选择。

答案 1 :(得分:10)

使用Enzyme测试浅层的高阶/装饰器 使用名为dive()的方法

点击此链接,了解潜水的工作原理

https://github.com/airbnb/enzyme/blob/master/docs/api/ShallowWrapper/dive.md

所以你可以用更高的顺序浅化组件,然后潜入内部。

在上面的示例中:

const wrapper=shallow(<PlayerProfile name={name} avatar={}/>)
expect(wrapper.find("PlayerProfile").dive().find(".player-profile").length).toBe(1)

同样,您可以访问属性并对其进行测试。

答案 2 :(得分:3)

你可以使用&#39; babel-plugin-remove-decorators&#39;插入。此解决方案可让您正常编写组件,而无需导出装饰和未装饰的组件。

首先安装插件,然后创建一个包含以下内容的文件,让我们称之为&#39; babelTestingHook.js&#39;

require('babel/register')({
 'stage': 2,
 'optional': [
  'es7.classProperties',
  'es7.decorators',
  // or Whatever configs you have
  .....
],
'plugins': ['babel-plugin-remove-decorators:before']
});

并运行下面的测试将忽略装饰器,您将能够正常测试组件

mocha ./tests/**/*.spec.js --require ./babelTestingHook.js --recursive

答案 3 :(得分:0)

在我的情况下装饰器是非常有用的,我不想在我的应用程序中摆脱它们(或返回包装和解包的版本)。

在我看来,最好的方法是使用babel-plugin-remove-decorators(可用于在测试中删除它们)Qusai说,但我写的预处理器不同,如下所示:

'use strict';

var babel = require('babel-core');

module.exports = {
  process: function(src, filename) {
    // Ignore files other than .js, .es, .jsx or .es6
    if (!babel.canCompile(filename)) {
     return '';
    }

    if (filename.indexOf('node_modules') === -1) {
      return babel.transform(src, {
        filename: filename, 
        plugins: ['babel-plugin-remove-decorators:before']
      }).code;
    }

    return src;
  }
};

注意babel.transform调用我将babel-plugin-remove-decorators:before元素作为数组值传递,请参阅:https://babeljs.io/docs/usage/options/

要将其与Jest(我使用的)联系起来,您可以使用package.json中的以下设置进行此操作:

"jest": {
  "rootDir": "./src",
  "scriptPreprocessor": "../preprocessor.js",
  "unmockedModulePathPatterns": [
    "fbjs",
    "react"
  ]
},

其中preprocessor.js是预处理器的名称。