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

nou*_*ine 4 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": ""
      };
   }
}
Run Code Online (Sandbox Code Playgroud)

和相关测试:

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!
       });
    });
Run Code Online (Sandbox Code Playgroud)

这种形式,当单击按钮时,仅使用发出警报alert

现在,当我运行测试时,我得到了:

expect(received).toBe(expected) // Object.is equality

Expected: 1
Received: 0
Run Code Online (Sandbox Code Playgroud)

这是一个失败,因为该模拟显然没有被调用。但是,我向您保证,表单组件在单击其按钮时确实会警告消息。

我怀疑由于某些原因,使用酶以编程方式执行单击时,组件window.alert不会使用该Form模型。

任何人?

Err*_*ame 9

相反window,您可以使用global.

global.alert = jest.fn();
Run Code Online (Sandbox Code Playgroud)

这是因为浏览器使用window名称,而nodejs使用global名称。


Est*_*ask 5

在带有JSDOM的Jest配置中global.window === global,因此可以对其进行模拟window

最好像这样嘲笑它

jest.spyOn(window, 'alert').mockImplementation(() => {});
Run Code Online (Sandbox Code Playgroud)

因为会window.alert = jest.fn()污染此套件中的其他测试。

黑盒测试的问题在于,故障排除难度更大,并且还依赖真实DOM预期的行为可能会导致问题,因为酶不一定支持这种行为。不知道实际问题是否handleSubmit被调用,alert未调用模拟仅是发生问题的证据。

在这种情况click下,按钮上的事件不会导致submit父表单上的事件,因为Enzyme 并非设计上支持它

正确的单元测试策略是为除测试对象(提交事件处理程序)以外的所有单元设置间谍程序或模拟程序。它通常涉及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(...);
Run Code Online (Sandbox Code Playgroud)

应直接使用formWrapper.setState而不是DOM事件模拟来更改状态。

一个更孤立的单元测试将是断言form所提供的预期onSubmit道具和formWrapper.instance()._handleSubmit(...)直接调用。