我正在关注本教程: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组件?
答案 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
是预处理器的名称。