我有以下React组件:
class Form extends React.Component {
constructor(props) {
super(props);
this.state = this._createEmptyTodo();
}
render() {
this.i18n = this.context;
return (
<div className="form">
<form onSubmit={this._handleSubmit.bind(this)}>
<input
placeholder={this.i18n.placeholders.addTitle}
type="text"
value={this.state.title}
onChange={this._handleTitleChange.bind(this)}></input>
<textarea
placeholder={this.i18n.placeholders.addDescription}
value={this.state.description}
onChange={this._handleDescriptionChange.bind(this)}></textarea>
<button>{this.i18n.buttons.submit}</button>
</form>
</div>
);
}
_handleTitleChange(e) {
this.setState({
title: e.target.value
});
}
_handleDescriptionChange(e) {
this.setState({
description: e.target.value
});
}
_handleSubmit(e) {
e.preventDefault();
var todo = {
date: new Date().getTime(),
title: this.state.title.trim(),
description: this.state.description.trim(),
done: false
};
if (!todo.title) {
alert(this.i18n.errors.title);
return;
}
if (!todo.description) {
alert(this.i18n.errors.description);
return;
}
this.props.showSpinner();
this.props.actions.addTodo(todo);
this.setState(this._createEmptyTodo());
}
_createEmptyTodo() {
return {
"pkey": null,
"title": "",
"description": ""
};
}
}
及相关测试:
const i18nContext = React.createContext();
Form.contextType = i18nContext;
describe('The <Form> component', () => {
var wrapper;
var showSpinner;
var actions = {}
beforeEach(() => {
showSpinner = jest.fn();
actions.addTodo = jest.fn();
wrapper = mount(<i18nContext.Provider value={i18n["en"]}>
<Form
showModalPanel={showSpinner}
actions={actions} />
</i18nContext.Provider>);
});
test("validate its input", () => {
window.alert = jest.fn();
wrapper.find("button").simulate("click");
expect(window.alert.mock.calls.length).toBe(1);//<<< this FAILS!
});
});
这种形式,当单击按钮时,它仅使用alert
来提醒消息。
现在,当我运行测试时,我得到了:
expect(received).toBe(expected) // Object.is equality
Expected: 1
Received: 0
这是失败的,因为没有明显调用该模拟。但是,我向您保证,表单组件在单击其按钮时确实会警告消息。
我怀疑由于某些原因,使用酶以编程方式执行点击时,window.alert
组件不会使用模拟的Form
。
有人吗?
答案 0 :(得分:1)
在具有JSDOM global.window === global
的Jest配置中,因此可以在window
上对其进行模拟。
最好像这样模拟它
jest.spyOn(window, 'alert').mockImplementation(() => {});
因为window.alert = jest.fn()
污染了该套件中的其他测试。
黑盒测试的问题在于,故障排除难度更大,并且还依赖真实DOM预期的行为可能会导致问题,因为酶不一定支持这种行为。未知是否未调用handleSubmit
模拟的实际问题alert
只是在证明出了问题。
在这种情况下,按钮上的click
事件不会在父表单上引起submit
事件,因为酶doesn't support that by design。
一种适当的单元测试策略是为除测试对象(即提交事件处理程序)以外的所有单元设置间谍程序或模拟程序。它通常涉及shallow
而不是mount
。
可能应该是:
jest.spyOn(window, 'alert').mockImplementation(() => {});
const formWrapper = wrapper.find(Form).dive();
jest.spyOn(formWrapper.instance(), '_handleSubmit');
formWrapper.find("form").simulate("submit");
expect(formWrapper.instance()._handleSubmit).toBeCalled();
expect(window.alert).toBeCalledWith(...);
应该直接使用formWrapper.setState
更改状态,而不是DOM事件模拟。
更孤立的单元测试是断言form
已提供了预期的onSubmit
属性,并直接调用formWrapper.instance()._handleSubmit(...)
。
答案 1 :(得分:0)
您可以使用window
代替global
。
global.alert = jest.fn();
这是因为浏览器使用window
名称,而nodejs使用global
名称。