Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I mock the window.alert method in jest?

I have the following React component:

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": ""
      };
   }
}

And the related test:

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!
       });
    });

This form, when the button gets clicked, it simply alerts a message using alert.

Now when I run the test I get this:

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

Expected: 1
Received: 0

Which is a failure because the mock does not get called apparently. But I promise you that the form component does alert a message when clicking on its button.

I suspect that, for some reasons, the mocked window.alert does not get used by the Form component when the click is performed programmatically using enzyme.

Anyone?

like image 693
nourdine Avatar asked Dec 04 '18 10:12

nourdine


People also ask

How to mock window alert jest?

alert = jest. fn(); wrapper. find("button"). simulate("click"); expect(window.

How do I test alert in react?

the alert isn't actually 'rendered' under test, so the only way (that I know of) is to inspect the mock and manually trigger the onPress that was passed to it.


2 Answers

In Jest configuration with JSDOM global.window === global, so it can be mocked on window.

It's preferable to mock it like

jest.spyOn(window, 'alert').mockImplementation(() => {});

because window.alert = jest.fn() contaminates other tests in this suite.

The problem with blackbox testing is that troubleshooting is harder, also relying on the behaviour that expected from real DOM may cause problems because Enzyme doesn't necessary support this behaviour. It's unknown whether the actual problem, handleSubmit was called or not, that alert mock wasn't called is just an evidence that something went wrong.

In this case click event on a button won't cause submit event on parent form because Enzyme doesn't support that by design.

A proper unit-testing strategy is to set up spies or mocks for all units except tested one, which is submit event handler. It usually involves shallow instead of mount.

It likely should be:

  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(...);

State should be changed directly with formWrapper.setState instead of DOM events simulation.

A more isolated unit test would be to assert that form was provided with expected onSubmit prop and call formWrapper.instance()._handleSubmit(...) directly.

like image 91
Estus Flask Avatar answered Oct 18 '22 03:10

Estus Flask


Instead of window, you can use global.

global.alert = jest.fn();

This is because browsers use the window name, while nodejs use the global name.

like image 6
Errorname Avatar answered Oct 18 '22 01:10

Errorname