如何在开玩笑中模拟window.alert方法?

时间:2018-12-04 10:41:42

标签: reactjs jestjs enzyme

我有以下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

有人吗?

2 个答案:

答案 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名称。