Simulate a button click in Jest
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);
});
});
Related videos on Youtube
foobar
Updated on November 13, 2021Comments
-
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 about 7 yearsHow do you know it didn't do anything? What are you checking next to see if the button click occurred?
-
foobar about 7 yearsGood question. I expect the error field to appear: const field = pageMounted.find('#notification'); expect(field.length).toBe(1);
-
Toby about 7 yearsHrm. Have you added a
console.warn
to the function that runs onClick to see if it fires in the Jest console? -
Andreas Köberle about 7 yearsCould you please add the code for the
MyCoolPage
component, otherwise its hard to figure out whats the actual problem is. -
foobar about 7 yearsThank 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 over 6 yearsThank 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 over 6 yearsOn the back of your first example, can you give an example of how we can write a test for an
onChange
function whosevalue
matches the input element'svalue
attribute? Thanks! -
Omortis about 5 yearsWhat does this actually test, though?
-
Jeremy Moritz over 4 yearsI have a button that calls my
handleClick
method when clicked. How do i test thathandleClick
was actually called when the button is clicked? -
Jeremy Moritz over 4 yearsWhat 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 over 4 years@JeremyMoritz that is why I don't understand the point or the logic on unit tests.
-
Brady Dowling about 4 yearsWhile 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 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 almost 4 yearsAren'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 over 3 yearsWhich previous answer? Or is it more than one (which ones?)?
-
Black over 3 years@PeterMortensen I have clarified the answer. Accepted answer is using enzyme simulate, which is going to be deprecated.
-
Hinrich over 3 yearsyou might need to call
wrapper.update()
after one of these, as enzyme might not be able to notice that a change happened. -
Oli over 3 yearsWhat about a button that does not have a
onClick
prop? Such as a button oftype="submit"
within a<form />
? Yes could callonSubmit
on the form - but this isn't ideal. Users will click the button, and that is what you want to test. -
nfroidure over 3 yearsJust 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 over 2 yearsWhat would be a way to achieve the same thing using Jest and @testing-library/user-event?
-
Carmine Tambascia about 2 yearsThe question asked regarding a button not a checkbox
-
gerardnico about 2 yearsIt 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.