Javascript 用酶don和x27进行单元试验;t重新绑定辅助函数的上下文
这是我在尝试使用AirBnB的React测试库重构一些React组件时遇到的一个有趣问题 我认为最好的解释我的问题的方法是通过一个例子 下面是一个小的React组件,它将根据从其父组件接收到的道具显示一条消息: test.js:Javascript 用酶don和x27进行单元试验;t重新绑定辅助函数的上下文,javascript,reactjs,enzyme,Javascript,Reactjs,Enzyme,这是我在尝试使用AirBnB的React测试库重构一些React组件时遇到的一个有趣问题 我认为最好的解释我的问题的方法是通过一个例子 下面是一个小的React组件,它将根据从其父组件接收到的道具显示一条消息: test.js: import React from 'react'; function renderInnerSpan() { const {foo} = this.props; if (foo) { return <span>Foo i
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>
);
}
}
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!');
});
});
这很好,但是测试组件的当前实现并没有它可能的那么高效。通过使用.call(this)
,每次调用render()
函数时,它都会创建一个新函数。我可以通过在组件的构造函数中绑定this
的正确上下文来避免这种情况,如下所示:
export default class extends React.Component {
constructor(props) {
super(props);
renderInnerSpan = renderInnerSpan.bind(this);
}
render() {
return (
<div>
{renderInnerSpan()}
</div>
);
}
}
我在构造函数中添加了一个console.log(props.foo)
,它确认了构造函数在我期望的时候仍然被调用,并且它接收的道具是正确的。但是,我在renderinerspan
中添加了console.log(foo)
,看起来该值一直都是真的,即使在重新渲染组件后,其foo
属性显式设置为false
看起来renderinerspan
只能绑定一次,而酵素在每次测试中都会重复使用它。那么,有什么好处呢?我正在测试中重新创建我的组件,它用我期望的值调用它的构造函数-为什么我的绑定renderinerspan
函数继续使用旧值
提前感谢您的帮助。我认为您在类之外定义了renderInnerSpan函数,这可能会产生一些问题 试试这个:
import React from 'react';
export default class extends React.Component {
render() {
return (
<div>
{this.renderInnerSpan.bind(this)}
</div>
);
}
renderInnerSpan() {
const {foo} = this.props;
if (foo) {
return <span>Foo is truthy!</span>;
}
return <span>Foo is falsy!</span>;
}
}
如果您在每次测试中都要更改道具,那么就没有理由在beforeach块中设置道具。只需在每个测试中使用新的renderComponent即可
it('should display the correct message for truthy values', () => {
renderedElement = renderComponent({foo: true});
const span = renderedElement.props().children;
expect(span.props.children).to.equal('Foo is truthy!');
});
这里的问题是,一个函数不能被绑定多次,正如您在测试用例中试图绑定的那样 原因是上下文不仅仅是函数本身的属性。当一个函数被绑定时,它相当于被包装在一个容器中 上下文(
this
-assignment)保存在外来对象的[[BoundThis]]
属性中。绑定函数将始终在此上下文中调用,即使它再次绑定
您可以自己进行测试:
函数foo(){
console.log(this.bar);
}
foo();//未定义
foo=foo.bind({bar:1});
foo();//1.
foo=foo.bind({bar:2});
foo();//1
没有理由不能在类的上下文之外定义renderinerspan
。正如我在最初的帖子中所说,最初的实现是有效的。仅仅因为你可以,并不意味着你应该。renderInnerSpan属于类的上下文,应该在类内部定义。这将使函数可以公开访问,这是我非常反对的。它的当前位置使函数私有,这在测试组件行为时很重要。无状态功能组件似乎是一种解决方法。我最终采取了一种稍微不同的方法,将我的renderinerspan
变成了一个SFC。谢谢你的解释,这现在更有意义了。
function renderComponent(expectedProps) {
const componentElement = React.createElement(Test, expectedProps);
return shallow(componentElement);
}
it('should display the correct message for truthy values', () => {
renderedElement = renderComponent({foo: true});
const span = renderedElement.props().children;
expect(span.props.children).to.equal('Foo is truthy!');
});