0
votes

I am trying to write a test that uses enzyme to simulate a button click which expects the onClick handler to be fired. I want to replace the component's onClick handler with a mock. When the test runs, the component class method is being called instead of the mock function. Any guidance would be appreciated!

Below is the test output:

  FAIL  src/Form.test.js
  ● Console

    console.log src/Form.js:5
      this is from the component

  ● Submit handler called on click

    expect(jest.fn()).toHaveBeenCalled()

    Expected number of calls: >= 1
    Received number of calls:    0

      14 |   submitButton.simulate("click");
      15 | 
    > 16 |   expect(onHandleSubmitMock).toHaveBeenCalled();
         |                              ^
      17 | });
      18 | 

      at Object.<anonymous>.test (src/Form.test.js:16:30)

package versions: "jest": 25.1.0 "jest-enzyme": 7.1.2 "react": 16.8.6

Form.js Component


    import React, { Component } from "react";

    export class Form extends Component {
      onHandleSubmit = () => {
        console.log("this is from the component");
      };

      render() {
        return (
          <div>
            <button data-test="submit" onClick={this.onHandleSubmit}>
              Submit
            </button>
          </div>
        );
      }
    }

    export default Form;

Form.test.js


    import React from "react";
    import { shallow } from "enzyme";

    import Form from "./Form";

    test("Submit handler called on click", () => {
      const onHandleSubmitMock = jest.fn(() => console.log("mock was called"));
      const wrapper = shallow(<Form />);

      wrapper.instance().onHandleSubmit = onHandleSubmitMock;
      wrapper.update();

      const submitButton = wrapper.find(`[data-test="submit"]`);
      submitButton.simulate("click");

      expect(onHandleSubmitMock).toHaveBeenCalled();
    });
1
I would strongly recommend not mocking parts of the thing you think you're testing. Mock out the collaborator (in this case, console.log).jonrsharpe
@jonrsharpe Thanks for the input. The console.log was added in an attempt to figure out what was going on with the test. My intent is to test to make sure onHandlerSubmit is called. isn't that the collaborator?Brad McAllister
No, it's part of the component you're testing. You should test through the public API, basically the props and the rendered DOM, not via the internals. Test behaviour, rather than implementation.jonrsharpe
Thanks @jonrsharpe! I am still trying to wrap my head around all this testing stuff. This is very helpful.Brad McAllister

1 Answers

0
votes

The reason is when shallow renders the Form component, the original onHandleSubmit method is already bound to onClick. So even if you assign the onHandleSubmitMock to the instance of the Form component. It's "late". When you simulate the click event, it will trigger the original onHandleSubmit method rather than the mocked one.

If you insist replace onHandleSubmit method with a mocked one. You need to make sure you replace the method before shallow rendering(bind to onClick)

E.g. form.jsx:

import React from 'react';
import { shallow } from 'enzyme';
import Form from './form';

test('Submit handler called on click', () => {
  const onHandleSubmitMock = jest.fn(() => console.log('mock was called'));
  Form.prototype.onHandleSubmit = onHandleSubmitMock;
  const wrapper = shallow(<Form />);
  const submitButton = wrapper.find(`[data-test="submit"]`);
  submitButton.simulate('click');

  expect(onHandleSubmitMock).toHaveBeenCalled();
});

form.test.jsx:

import React, { Component } from 'react';

export class Form extends Component {
  constructor() {
    this.onHandleSubmit = this.onHandleSubmit.bind(this);
  }
  onHandleSubmit() {
    console.log('this is from the component');
  }

  render() {
    return (
      <div>
        <button data-test="submit" onClick={this.onHandleSubmit}>
          Submit
        </button>
      </div>
    );
  }
}

export default Form;

Unit test results with coverage report:

 PASS  stackoverflow/60138901/form.test.jsx (6.937s)
  ✓ Submit handler called on click (44ms)

  console.log stackoverflow/60138901/form.test.jsx:6
    mock was called

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |   91.67 |      100 |      75 |      90 |                   
 form.jsx |   91.67 |      100 |      75 |      90 | 8                 
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.65s