我正在为新的React应用编写测试,我对该项目的部分意图是完全了解该测试内容-它已经存在了一段时间,但我从未将其投入生产。
到目前为止,已经使用快照以及其他静态和同步方法编写了大量测试。直到现在我正在处理setState-> Expect(postFunctionState).toEqual(desiredState)情况,并且当我在控制台中记录日志流的方式并可以看到setState()正在被调用时,这似乎一直有效可以在浏览器中看到结果,我似乎无法编写可复制行为的测试。
以下是相关代码:
//Component (extracted):
export class CorsetCreator extends React.Component {
constructor(props) {
super(props);
this.state = {
productName: '',
productType: 'Overbust',
enabled: false,
created: false,
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleNameChange = this.handleNameChange.bind(this);
this.handleProductChange = this.handleProductChange.bind(this);
}
handleNameChange(e) {
this.setState({ productName: e.target.value });
this.handleChange.bind(this)(e);
}
handleProductChange(e) {
this.setState({ productType: e.target.value });
this.handleChange.bind(this)(e);
}
handleChange(e) {
e.preventDefault();
if (e.target.value === '') {
this.setState({ enabled: false }); //User should not be able to submit an empty product name
return;
}
const { corsets } = this.props.corsetGallery;
if (!corsets) {
this.forceUpdate();
this.setState({ enabled: true }); //Point of this exercise is to guarantee unique combos of name&type. If there are no pre-existing corsets then no need to test for uniqueness
return;
}
const productType =
e.target.value === 'Underbust' || e.target.value === 'Overbust'
? e.target.value
: this.state.productType;
const productName =
e.target.value === 'Underbust' || e.target.value === 'Overbust'
? this.state
: e.target.value;
const filteredCorsets = corsets.filter(
corset => corset.type === productType && corset.name === productName,
);
this.setState({
enabled: !(filteredCorsets && filteredCorsets.length > 0),
});
}
//Test (extracted)
it('handles statechanges correctly with a valid new corset', () => {
const store = configureStore({}, browserHistory);
const creator = mount(
<Provider store={store}>
<CorsetCreator />
</Provider>,
);
const namebox = creator.find('NameBox').at(0);
const nameBoxField = namebox.find('input').at(0);
const submitbutton = creator.find('SubmitButton').at(0);
creator.setState({ enabled: false });
expect(submitbutton.props().enabled).toEqual(false);
nameBoxField.simulate('change', { target: { value: 'Test' } });
creator.update();
expect(creator.state().enabled).toEqual(true);
});
因为setState是异步的,所以我觉得某种回调或promise可能是这里的解决方案,但我已经尝试过这两种方法,并且似乎无法通过最佳方式进行排序。考虑这种情况的最佳方法是什么?
答案 0 :(得分:1)
TL; DR:记住React组件是函数。在他们所有的荣耀中,他们接受道具,并且您会收到render()
函数的输出。测试输出。
如果状态中有变量,则很可能会将其传递给子组件或操纵当前组件的可视输出。或说状态中的项目将毫无用处:)
测试状态是多余的,就像测试React本身一样。
通常会引起关注的一个问题是“但是我正在通过使用setState(...)
显示/隐藏该元素”,或者“我正在将状态作为道具传递给一个孩子”。
编写测试时,渲染组件。模拟动作并检查render函数的输出是否已更改。
以该组件为例:
class TextWithClick extends React.Component {
state={ count: 0 };
handleClick = () => {
this.setState({ count: this.state.count + 1})
}
render() {
return (
<input
value={this.state.count} {/* state passed down as props */}
onClick={this.handleClick}
/>
)
}
}
ReactDOM.render(<TextWithClick/>
, document.getElementById('root'))
很简单。单击输入字段,增加它显示的文本,这是一个道具。以下是一些测试断言:
// using enzyme
it("should increase the count", () => {
const wrapper = shallow(<TextWithClick />);
const input = wrapper.find("input").at(0);
// test props
input.simulate("click");
expect(input.props().value).toEqual(1);
input.simulate("click");
expect(input.props().value).toEqual(2);
input.simulate("click");
expect(input.state().count).toEqual(3); // this tests React more than your component logic :)
});
记住React组件是函数。在他们所有的荣耀中,他们接受道具,并且您会收到render()
函数的输出。测试输出。
对于Redux,也是一样。测试状态更改就像测试Redux的connect()功能一样。 Mozilla使用real redux store to test their app。即测试最终输出。
我从上面的链接中引用了有关测试React / Redux应用程序的信息(似乎无法在SO中多行显示blockquotes:
一篇好文章:Anthony Ng的Testing React Component’s State。