Simulate a button click in Jest

281,123

Solution 1

#1 Using Jest

This is how I use the Jest mock callback function to test the click event:

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

describe('Test Button component', () => {
  it('Test click event', () => {
    const mockCallBack = jest.fn();

    const button = shallow((<Button onClick={mockCallBack}>Ok!</Button>));
    button.find('button').simulate('click');
    expect(mockCallBack.mock.calls.length).toEqual(1);
  });
});

I am also using a module called enzyme. Enzyme is a testing utility that makes it easier to assert and select your React Components

#2 Using Sinon

Also, you can use another module called Sinon which is a standalone test spy, stubs and mocks for JavaScript. This is how it looks:

import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';

import Button from './Button';

describe('Test Button component', () => {
  it('simulates click events', () => {
    const mockCallBack = sinon.spy();
    const button = shallow((<Button onClick={mockCallBack}>Ok!</Button>));

    button.find('button').simulate('click');
    expect(mockCallBack).toHaveProperty('callCount', 1);
  });
});

#3 Using Your own Spy

Finally, you can make your own naive spy (I don't recommend this approach unless you have a valid reason for that).

function MySpy() {
  this.calls = 0;
}

MySpy.prototype.fn = function () {
  return () => this.calls++;
}

it('Test Button component', () => {
  const mySpy = new MySpy();
  const mockCallBack = mySpy.fn();

  const button = shallow((<Button onClick={mockCallBack}>Ok!</Button>));

  button.find('button').simulate('click');
  expect(mySpy.calls).toEqual(1);
});

Solution 2

Solutions in accepted answer are being deprecated

#4 Calling prop directly

Enzyme simulate is supposed to be removed in version 4. The main maintainer is suggesting directly invoking prop functions, which is what simulate does internally. One solution is to directly test that invoking those props does the right thing; or you can mock out instance methods, test that the prop functions call them, and unit test the instance methods.

You could call click, for example:

wrapper.find('Button').prop('onClick')() 

Or

wrapper.find('Button').props().onClick() 

Information about deprecation: Deprecation of .simulate() #2173

Solution 3

Using Jest, you can do it like this:

test('it calls start logout on button click', () => {
    const mockLogout = jest.fn();
    const wrapper = shallow(<Component startLogout={mockLogout}/>);
    wrapper.find('button').at(0).simulate('click');
    expect(mockLogout).toHaveBeenCalled();
});

Solution 4

Testing-library makes this easy for you with the click function.

It's part of the user-event library that can be used with every dom environment (react, jsdom, browser, ...)

The example from the doc:

import React from 'react'
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'

test('click', () => {
  render(
    <div>
      <label htmlFor="checkbox">Check</label>
      <input id="checkbox" type="checkbox" />
    </div>,
  )

  userEvent.click(screen.getByText('Check'))
  expect(screen.getByLabelText('Check')).toBeChecked()
})

Solution 5

I needed to do a little bit of testing myself of a button component. These tests work for me ;-)

import { shallow } from "enzyme";
import * as React from "react";
import Button from "../button.component";

describe("Button Component Tests", () => {
    it("Renders correctly in DOM", () => {
        shallow(
            <Button text="Test" />
        );
    });
    it("Expects to find button HTML element in the DOM", () => {
        const wrapper = shallow(<Button text="test"/>)
        expect(wrapper.find('button')).toHaveLength(1);
    });

    it("Expects to find button HTML element with className test in the DOM", () => {
        const wrapper = shallow(<Button className="test" text="test"/>)
        expect(wrapper.find('button.test')).toHaveLength(1);
    });

    it("Expects to run onClick function when button is pressed in the DOM", () => {
        const mockCallBackClick = jest.fn();
        const wrapper = shallow(<Button onClick={mockCallBackClick} className="test" text="test"/>);
        wrapper.find('button').simulate('click');
        expect(mockCallBackClick.mock.calls.length).toEqual(1);
    });
});
Share:
281,123

Related videos on Youtube

foobar
Author by

foobar

Updated on November 13, 2021

Comments

  • foobar
    foobar over 2 years

    Simulating a button click seems like a very easy/standard operation. Yet, I can't get it to work in Jest.js tests.

    This is what I tried (and also doing it using jQuery), but it didn't seem to trigger anything:

    import { mount } from 'enzyme';
    
    page = <MyCoolPage />;
    pageMounted = mount(page);
    
    const button = pageMounted.find('#some_button');
    expect(button.length).toBe(1); // It finds it alright
    button.simulate('click'); // Nothing happens
    
    • Toby
      Toby about 7 years
      How do you know it didn't do anything? What are you checking next to see if the button click occurred?
    • foobar
      foobar about 7 years
      Good question. I expect the error field to appear: const field = pageMounted.find('#notification'); expect(field.length).toBe(1);
    • Toby
      Toby about 7 years
      Hrm. Have you added a console.warn to the function that runs onClick to see if it fires in the Jest console?
    • Andreas Köberle
      Andreas Köberle about 7 years
      Could you please add the code for the MyCoolPage component, otherwise its hard to figure out whats the actual problem is.
    • foobar
      foobar about 7 years
      Thank you guys for the tips. I found my problem, thanks to your questions. I basically did a small test with a simple button and it worked: MyCoolPage = ( <button type="submit" id="cool_button" onClick={() => { console.warn('I was clicked');}>Cool Button</button> ); I then realized that my button belonged to the redux-form, so it didn't have onClick, but onSubmit instead, so adding button.simulate('submit'); solved the issue. Thanks again for your feedback!
  • foobar
    foobar over 6 years
    Thank you for a detailed answer Saman! This is very useful when you can pass onClick method directly into the component you are testing, and I will use your code as a reference for that :). I think in my example though I couldn't really pass onClick and I had to rely on other clues to know that the button was clicked.
  • blankface
    blankface over 6 years
    On the back of your first example, can you give an example of how we can write a test for an onChange function whose value matches the input element's value attribute? Thanks!
  • Omortis
    Omortis about 5 years
    What does this actually test, though?
  • Jeremy Moritz
    Jeremy Moritz over 4 years
    I have a button that calls my handleClick method when clicked. How do i test that handleClick was actually called when the button is clicked?
  • Jeremy Moritz
    Jeremy Moritz over 4 years
    What is the value in creating a complete button within your tests with a mocked callback when clicked and then clicking that button in the test? Like most testing examples I've seen, you haven't even tested a single line of your actual code when you do this.
  • user3808307
    user3808307 over 4 years
    @JeremyMoritz that is why I don't understand the point or the logic on unit tests.
  • Brady Dowling
    Brady Dowling about 4 years
    While it does answer the question for React, most of this answer is more related to mocking than it is to simulating a button click.
  • shan
    shan almost 4 years
    @Saman Shafigh how would this work if lets say the button is nested two levels down? So the click handler is being passed from first component to 2nd component, then eventually to the button.
  • B-Lat
    B-Lat almost 4 years
    Aren't all of the above examples just testing that HTML works? i.e. if i create a button and assign it a click event, it will call that click event? It's not unit testing anything specific to our code.
  • Peter Mortensen
    Peter Mortensen over 3 years
    Which previous answer? Or is it more than one (which ones?)?
  • Black
    Black over 3 years
    @PeterMortensen I have clarified the answer. Accepted answer is using enzyme simulate, which is going to be deprecated.
  • Hinrich
    Hinrich over 3 years
    you might need to call wrapper.update() after one of these, as enzyme might not be able to notice that a change happened.
  • Oli
    Oli over 3 years
    What about a button that does not have a onClick prop? Such as a button of type="submit" within a <form />? Yes could call onSubmit on the form - but this isn't ideal. Users will click the button, and that is what you want to test.
  • nfroidure
    nfroidure over 3 years
    Just in case someone hits the same issue, if you have to handle the event, this can be usefull: ``` lang-js act(() => { component.root.findByType('button').props.onClick({ preventDefault: jest.fn(), stopPropagation: jest.fn(), }); }); ```
  • Arijit Ghosh
    Arijit Ghosh over 2 years
    What would be a way to achieve the same thing using Jest and @testing-library/user-event?
  • Carmine Tambascia
    Carmine Tambascia about 2 years
    The question asked regarding a button not a checkbox
  • gerardnico
    gerardnico about 2 years
    It does not matter as you can click all elements. Just change the label from the example with a button tag and it will work the same.