这是我在尝试使用 AirBnB 的 React 测试库Enzyme重构我的一些 React 组件时遇到的一个有趣的问题。
我认为解释我的问题的最好方法是通过一个例子。
这是一个小的 React 组件,它将根据从其父组件接收到的 props 显示一条消息:
测试.js:
import React from 'react';
function renderInnerSpan() {
const {foo} = this.props;
if (foo) {
return <span>Foo is truthy!</span>;
}
return <span>Foo is falsy!</span>;
}
export default class extends React.Component {
render() {
return (
<div>
{renderInnerSpan.call(this)}
</div>
);
}
}
这是该组件的测试套件,其中包含两个通过测试:
test.spec.js:
import Test from '../../src/test';
import React from 'react';
import {shallow} from 'enzyme';
import {expect} from 'chai';
describe('Test Suite', () => {
let renderedElement,
expectedProps;
function renderComponent() {
const componentElement = React.createElement(Test, expectedProps);
renderedElement = shallow(componentElement);
}
beforeEach(() => {
expectedProps = {
foo: true
};
renderComponent();
});
it('should display the correct message for truthy values', () => {
const span = renderedElement.props().children;
expect(span.props.children).to.equal('Foo is truthy!');
});
it('should display the correct message for falsy values', () => {
expectedProps.foo = false;
renderComponent();
const span = renderedElement.props().children;
expect(span.props.children).to.equal('Foo is falsy!');
});
});
这可以正常工作,但 Test 组件的当前实现并没有达到应有的效率。通过使用.call(this)
,每次render()
调用函数时都会创建一个新函数。this
我可以通过在组件的构造函数中绑定正确的上下文来避免这种情况,如下所示:
export default class extends React.Component {
constructor(props) {
super(props);
renderInnerSpan = renderInnerSpan.bind(this);
}
render() {
return (
<div>
{renderInnerSpan()}
</div>
);
}
}
进行此更改后,组件仍按预期工作,但测试开始失败:
AssertionError: expected 'Foo is truthy!' to equal 'Foo is falsy!'
Expected :Foo is falsy!
Actual :Foo is truthy!
我console.log(props.foo)
在构造函数中添加了一个,它确认构造函数在我期望的时候仍然被调用,并且它接收的道具是正确的。但是,我添加了一个console.log(foo)
inside renderInnerSpan
,并且看起来该值始终为 true,即使在重新渲染组件并将其foo
prop 明确设置为false
.
看起来renderInnerSpan
只绑定一次,Enzyme 在每次测试中都重复使用它。那么,什么给了?我正在测试中重新创建我的组件,它使用我期望的值调用它的构造函数 - 为什么我的绑定renderInnerSpan
函数继续使用旧值?
在此先感谢您的帮助。