使用酶测试对 React 组件状态的更改并监视实例方法
Testing changes to React component state and spying on instance methods using enzyme
我正在开发一个包装器组件,以便在 React 中顺利加载图像。我将 enzyme 与 mocha、chai 和 sinon 一起使用来对我的组件进行单元测试。在这里的测试中,我试图测试:
组件的状态在图像加载后更新
调用了组件上的 onLoad
实例方法
const wrapper = shallow( );
const onLoad = wrapper.find('img').props().onLoad;
const onLoadSpy = sinon.spy(onLoad); wrapper.update();
const status = wrapper.state().status;
期望(onLoadSpy)。to.have.been.called;
期望(状态)。to.equal('LOADED');
我发现状态的更新既没有被酶反映出来,也没有更新 onLoad
间谍的调用计数。这是测试对应的代码:
export default class Image extends Component {
constructor(props) {
super(props);
if (props.src != null && typeof props.src === 'string') {
this.state = {
status: LOADING,
};
} else {
this.state = {
status: PENDING,
};
}
this.onLoad = this.onLoad.bind(this);
}
onLoad() {
this.setState({
status: LOADED,
});
}
render() {
//lots of code above the part we care about
const defaultImageStyle = style({
opacity: 0,
transisition: 'opacity 150ms ease',
});
const loadedImageStyle = style({
opacity: 1,
});
let imageStyle = defaultImageStyle;
if (this.state.status === LOADED) {
imageStyle = merge(defaultImageStyle, loadedImageStyle);
} else {
imageStyle = defaultImageStyle;
}
let image;
if (alt != null) {
image = (<img
className={imageStyle}
src={src}
width={width}
height={height}
alt={alt}
onLoad={this.onLoad}
/>);
} else {
image = (<img
className={imageStyle}
src={src}
width={width}
height={height}
role="presentation"
onLoad={this.onLoad}
/>);
}
let statusIndicator = null;
if (this.state.status === LOADING) {
statusIndicator = (<div className={loadingStyle}></div>);
}
return (<div className={wrapperStyle}>
{statusIndicator}
{image}
</div>);
}}
查看完整代码以获得更好的上下文:
可以在不依赖 sinon
的情况下进行测试。通过预期 onLoad
和 onFire
事件侦听器被调用,测试检查 img
是否触发了 load
和 error
事件。
相反,simulate
img
的事件使用 enzyme
并检查是否发生了适当的状态转换:
it('has a state of LOADED if a good src prop is supplied', () => {
const wrapper = shallow(<Image
src="anyString.jpg"
width={300}
height={300}
/>);
const img = wrapper.find('img');
img.simulate('load');
const status = wrapper.state().status;
expect(status).to.equal('LOADED');
});
这也消除了 mount
组件的需要。可以找到更新的测试 here.
我看到这种方法的主要问题是状态是内部的东西,而不是应该在组件外部知道的东西。现在您正在将状态信息(在本例中为 "status")泄漏到测试中。
这样做意味着您没有在进行 "black-box testing",这是最有价值的测试类型。您正在泄漏组件的实现细节。也就是说"Encapsulation"应该被高度重视。
也许有更好的方法来测试它。例如,您也可以导出一个展示组件,它将您需要测试的状态部分作为道具。或者使用酶 find 方法寻找当状态为 "LOADED" 时将呈现的元素。
我正在开发一个包装器组件,以便在 React 中顺利加载图像。我将 enzyme 与 mocha、chai 和 sinon 一起使用来对我的组件进行单元测试。在这里的测试中,我试图测试:
组件的状态在图像加载后更新
调用了组件上的
onLoad
实例方法
const wrapper = shallow( ); const onLoad = wrapper.find('img').props().onLoad; const onLoadSpy = sinon.spy(onLoad); wrapper.update(); const status = wrapper.state().status; 期望(onLoadSpy)。to.have.been.called; 期望(状态)。to.equal('LOADED');
我发现状态的更新既没有被酶反映出来,也没有更新 onLoad
间谍的调用计数。这是测试对应的代码:
export default class Image extends Component {
constructor(props) {
super(props);
if (props.src != null && typeof props.src === 'string') {
this.state = {
status: LOADING,
};
} else {
this.state = {
status: PENDING,
};
}
this.onLoad = this.onLoad.bind(this);
}
onLoad() {
this.setState({
status: LOADED,
});
}
render() {
//lots of code above the part we care about
const defaultImageStyle = style({
opacity: 0,
transisition: 'opacity 150ms ease',
});
const loadedImageStyle = style({
opacity: 1,
});
let imageStyle = defaultImageStyle;
if (this.state.status === LOADED) {
imageStyle = merge(defaultImageStyle, loadedImageStyle);
} else {
imageStyle = defaultImageStyle;
}
let image;
if (alt != null) {
image = (<img
className={imageStyle}
src={src}
width={width}
height={height}
alt={alt}
onLoad={this.onLoad}
/>);
} else {
image = (<img
className={imageStyle}
src={src}
width={width}
height={height}
role="presentation"
onLoad={this.onLoad}
/>);
}
let statusIndicator = null;
if (this.state.status === LOADING) {
statusIndicator = (<div className={loadingStyle}></div>);
}
return (<div className={wrapperStyle}>
{statusIndicator}
{image}
</div>);
}}
查看完整代码以获得更好的上下文:
可以在不依赖 sinon
的情况下进行测试。通过预期 onLoad
和 onFire
事件侦听器被调用,测试检查 img
是否触发了 load
和 error
事件。
相反,simulate
img
的事件使用 enzyme
并检查是否发生了适当的状态转换:
it('has a state of LOADED if a good src prop is supplied', () => {
const wrapper = shallow(<Image
src="anyString.jpg"
width={300}
height={300}
/>);
const img = wrapper.find('img');
img.simulate('load');
const status = wrapper.state().status;
expect(status).to.equal('LOADED');
});
这也消除了 mount
组件的需要。可以找到更新的测试 here.
我看到这种方法的主要问题是状态是内部的东西,而不是应该在组件外部知道的东西。现在您正在将状态信息(在本例中为 "status")泄漏到测试中。
这样做意味着您没有在进行 "black-box testing",这是最有价值的测试类型。您正在泄漏组件的实现细节。也就是说"Encapsulation"应该被高度重视。
也许有更好的方法来测试它。例如,您也可以导出一个展示组件,它将您需要测试的状态部分作为道具。或者使用酶 find 方法寻找当状态为 "LOADED" 时将呈现的元素。