Symuluj kliknięcie przycisku w Jest

84

Symulacja kliknięcia przycisku wydaje się bardzo łatwą / standardową operacją. Jednak nie mogę zmusić go do działania w testach Jest.js.

Oto, co próbowałem (i robię to również przy użyciu jQuery), ale nie wydawało się, aby cokolwiek wyzwalało:

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
foobar
źródło
Skąd wiesz, że to nic nie dało? Co teraz sprawdzasz, aby zobaczyć, czy nastąpiło kliknięcie przycisku?
Toby
Dobre pytanie. Spodziewam się pojawienia się pola błędu: const field = pageMounted.find ('# notification'); oczekiwać (długość pola) .toBe (1);
foobar
Hrm. Czy dodałeś console.warndo funkcji, która działa onClick, aby sprawdzić, czy zadziała w konsoli Jest?
Toby
Czy mógłbyś dodać kod MyCoolPage komponentu, w przeciwnym razie trudno będzie ustalić, jaki jest rzeczywisty problem.
Andreas Köberle
1
Dziękuję za wskazówki. Znalazłem swój problem dzięki Twoim pytaniom. Zasadniczo zrobiłem mały test za pomocą prostego przycisku i zadziałał: MyCoolPage = (<button type = "submit" id = "cool_button" onClick = {() => {console.warn ('Zostałem kliknięty');}> Przycisk Cool </button>); Wtedy zdałem sobie sprawę, że mój przycisk należał do formularza redux, więc nie miał onClick, ale zamiast tego onSubmit, więc dodałem button.simulate ('submit'); rozwiązał problem. Jeszcze raz dziękuję za opinię!
foobar

Odpowiedzi:

137

# 1 Używanie Jest

Oto jak używam funkcji wywołania zwrotnego Jest mock do testowania zdarzenia kliknięcia:

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

Używam również modułu o nazwie enzym . Enzyme to narzędzie testowe, które ułatwia sprawdzanie i wybieranie komponentów React

# 2 Korzystanie z Sinon

Możesz także użyć innego modułu o nazwie Sinon, który jest samodzielnym szpiegiem testowym, kodami i próbami dla JavaScript. Tak to wygląda:

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 Używanie własnego szpiega

Wreszcie możesz stworzyć własnego naiwnego szpiega (nie polecam tego podejścia, chyba że masz ku temu ważny powód).

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);
});
Saman Shafigh
źródło
1
Dziękuję za szczegółową odpowiedź Saman! Jest to bardzo przydatne, gdy możesz przekazać metodę onClick bezpośrednio do testowanego komponentu, a ja użyję twojego kodu jako odniesienia do tego :). Myślę, że w moim przykładzie nie mogłem tak naprawdę przejść dalejClick i musiałem polegać na innych wskazówkach, aby wiedzieć, że przycisk został kliknięty.
foobar
Czy na odwrocie pierwszego przykładu możesz podać przykład, jak możemy napisać test dla onChangefunkcji, która valuepasuje do valueatrybutu elementu wejściowego ? Dzięki!
blankface
7
Ale co to właściwie sprawdza?
Omortis
1
Mam przycisk, który wywołuje moją handleClickmetodę po kliknięciu. Jak sprawdzić, czy handleClickrzeczywiście został wywołany po kliknięciu przycisku?
Jeremy Moritz
Chociaż odpowiada na pytanie dla Reacta, większość tej odpowiedzi jest bardziej związana z kpiną niż z symulacją kliknięcia przycisku.
Brady Dowling
21

Rozwiązania w zaakceptowanej odpowiedzi są przestarzałe

# 4 Bezpośrednie wywołanie rekwizytu

Enzyme simulate ma zostać usunięty w wersji 4. Główny opiekun sugeruje bezpośrednie wywoływanie funkcji prop, co jest tym, co simulate robi wewnętrznie. Jednym z rozwiązań jest bezpośrednie sprawdzenie, czy wywołanie tych właściwości działa właściwie; lub możesz wyszydzać metody instancji, sprawdzić, czy wywołują je funkcje prop, i przetestować jednostkowe metody instancji.

Możesz nazwać kliknięcie, na przykład:

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

Lub

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

Informacje o wycofaniu: Wycofanie .simulate () # 2173

czarny
źródło
Która poprzednia odpowiedź? A może więcej niż jeden (które?)?
Peter Mortensen
1
@PeterMortensen Wyjaśniłem odpowiedź. Zaakceptowaną odpowiedzią jest użycie symulacji enzymu, która zostanie wycofana.
Czarny
może być konieczne wywołanie wrapper.update()jednego z nich, ponieważ enzym może nie być w stanie zauważyć, że nastąpiła zmiana.
Hinrich
A co z przyciskiem, który nie ma onClickrekwizytu? Takich jak przycisk type="submit"wewnątrz <form />? Tak, można przywołać onSubmitformularz - ale to nie jest idealne. Użytkownicy klikną przycisk i właśnie to chcesz przetestować.
Oli
Dzięki @Black, ratownik!
Anas Latique
12

Używając Jest możesz to zrobić w następujący sposób:

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();
});
Jackgisel
źródło
7
Jaka jest wartość w tworzeniu pełnego przycisku w testach z fałszywym wywołaniem zwrotnym po kliknięciu, a następnie kliknięciu tego przycisku w teście? Jak większość przykładów testowych, które widziałem, nie przetestowałeś nawet ani jednej linii swojego rzeczywistego kodu, kiedy to robisz.
Jeremy Moritz
2
@JeremyMoritz, dlatego nie rozumiem sensu ani logiki testów jednostkowych.
user3808307
0

Możesz użyć czegoś takiego, aby wywołać program obsługi napisany po kliknięciu:

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

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

// The below line will execute your click function
pageMounted.instance().yourOnClickFunction();
utkarsh
źródło
0

Oprócz rozwiązań, które zasugerowano w komentarzach dla rodzeństwa, możesz nieco zmienić podejście do testowania i przetestować nie całą stronę naraz (z głębokim drzewem komponentów potomnych), ale wykonać test izolowanych komponentów. Uprości to testowanie onClick()i podobnych zdarzeń (patrz przykład poniżej).

Chodzi o to, aby testować tylko jeden komponent naraz, a nie wszystkie razem. W tym przypadku wszystkie komponenty potomne będą mockowane za pomocą funkcji jest.mock () .

Oto przykład, jak onClick()zdarzenie można przetestować w izolowanym SearchFormkomponencie przy użyciu Jest i mechanizmu renderującego reakcję .

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);
  });
});
Oleksii Trekhleb
źródło
0

Musiałem trochę przetestować komponent przycisku. Te testy działają na mnie ;-)

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);
    });
});
Hannibal B. Moulvad
źródło