Імітуйте клацання кнопки в Jest


84

Імітація клацання кнопки здається дуже простою / стандартною операцією. Проте я не можу змусити його працювати в тестах Jest.js.

Це те, що я спробував (і також робив це за допомогою jQuery), але, здається, це нічого не викликало:

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

Звідки ви знаєте, що воно нічого не зробило? Що ви перевіряєте далі, щоб побачити, чи не відбулося клацання кнопки?
Тобі

Гарне питання. Я очікую, що з’явиться поле помилки: const field = pageMount.find ('# сповіщення'); очікувати (field.length) .toBe (1);
foobar

Хв. Чи додали ви console.warnдо функції, яка працює на Click, щоб перевірити, чи спрацьовує вона на консолі Jest?
Тобі

Не могли б ви додати код MyCoolPage компонента, інакше важко зрозуміти, в чому проблема.
Андреас Коберле,

1
Дякую, хлопці, за поради. Я знайшов свою проблему, завдяки вашим запитанням. В основному я зробив невеликий тест за допомогою простої кнопки, і він спрацював: MyCoolPage = (<button type = "submit" id = "cool_button" onClick = {() => {console.warn ('Мене натиснули');}> Кнопка "Cool" </button>); Потім я зрозумів, що моя кнопка належить до форми redux, тож вона мала onClick, а натомість onSubmit, тому додавши button.simulate ('submit'); вирішив питання. Ще раз спасибі за ваш відгук!
foobar

Відповіді:


138

# 1 Використання Jest

Ось як я використовую функцію зворотного виклику Jest для тестування події кліку:

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

1
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.
foobar

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!
blankface

7
What does this actually test, though?
Omortis

1
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

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.
Brady Dowling

21

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


Which previous answer? Or is it more than one (which ones?)?
Peter Mortensen

1
@PeterMortensen I have clarified the answer. Accepted answer is using enzyme simulate, which is going to be deprecated.
Black

you might need to call wrapper.update() after one of these, as enzyme might not be able to notice that a change happened.
Hinrich

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.
Oli

Thanks @Black, life saver!
Anas Latique

12

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

7
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.
Jeremy Moritz

2
@JeremyMoritz that is why I don't understand the point or the logic on unit tests.
user3808307

0

You may use something like this to call the handler written on click:

import { shallow } from 'enzyme'; // Mount is not required

page = <MyCoolPage />;
pageMounted = shallow(page);

// The below line will execute your click function
pageMounted.instance().yourOnClickFunction();

0

Additionally to the solutions that were suggested in sibling comments, you may change your testing approach a little bit and test not the whole page all at once (with a deep children components tree), but do an isolated component testing. This will simplify testing of onClick() and similar events (see example below).

The idea is to test only one component at a time and not all of them together. In this case all children components will be mocked using the jest.mock() function.

Here is an example of how the onClick() event may be tested in an isolated SearchForm component using Jest and react-test-renderer.

import React from 'react';
import renderer from 'react-test-renderer';
import { SearchForm } from '../SearchForm';

describe('SearchForm', () => {
  it('should fire onSubmit form callback', () => {
    // Mock search form parameters.
    const searchQuery = 'kittens';
    const onSubmit = jest.fn();

    // Create test component instance.
    const testComponentInstance = renderer.create((
      <SearchForm query={searchQuery} onSearchSubmit={onSubmit} />
    )).root;

    // Try to find submit button inside the form.
    const submitButtonInstance = testComponentInstance.findByProps({
      type: 'submit',
    });
    expect(submitButtonInstance).toBeDefined();

    // Since we're not going to test the button component itself
    // we may just simulate its onClick event manually.
    const eventMock = { preventDefault: jest.fn() };
    submitButtonInstance.props.onClick(eventMock);

    expect(onSubmit).toHaveBeenCalledTimes(1);
    expect(onSubmit).toHaveBeenCalledWith(searchQuery);
  });
});

0

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);
    });
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.