Umstellung von enzyme auf testing-library/react

Im Dezember 2021 gaben die Entwickler der sehr beliebten Unit-Testing-Bibliothek für React-Komponenten enzyme bekannt, dass sie die Entwicklung des Pakets einstellen werden. Wie in diesem Artikel auf dev.to erwähnt, führte React 18 zu viele bahnbrechende Änderungen ein und die Entwicklung einer kompatiblen Enzyme-Version bedeutete eine riesige, nicht durchführbare Überarbeitung des Pakets.

Da viele geOps-Anwendungen auf Enzyme für Unit-Tests angewiesen waren, musste eine alternative Lösung für Unit-Tests gefunden werden, um in Zukunft auf React 18 upgraden zu können. Glücklicherweise gibt es eine alternative Bibliothek, die auch offiziell von den React-Entwicklern empfohlen wird: testing-library/react.

Zunächst einmal ist es wichtig, einen wesentlichen Unterschied zwischen den beiden Bibliotheken zu benennen. Abgesehen von den DOM-Testmethoden bietet enzyme Test-Utilities, die auf den Komponentenzustand zugreifen und das Testen von Komponenten basierend auf ihren internen APIs ermöglichen. Stattdessen konzentriert sich testing-library/react nur auf das Testen der eigentlichen DOM-Elemente. Die Entwickler argumentieren, dass dieser Ansatz benutzerorientierter ist, da das DOM die endgültige Ausgabe ist, mit der der Benutzer tatsächlich interagiert. Dieser Ansatz vermeidet auch, dass der Test nach strukturellen Änderungen, die keinen Einfluss auf die Funktion der Komponente und die DOM-Ausgabe haben, angepasst werden muss, was sehr zeitaufwändig sein kann.

Eine ausführliche Dokumentation und ein Migrationsleitfaden beschreiben, wie enzyme-Tests mit testing-library/react umgeschrieben werden können. testing-library/react bietet eine Auswahl an Abfragefunktionen, die für eine benutzerorientierte Auswahl der DOM-Elemente  verwendet werden können.

In den folgenden Beispielen werden einige Tests in enzyme und umgeschrieben in testing-library/react verglichen.

Snapshots

Bei der Überprüfung von HTML-Baum-Snapshots testet testing-library/react das native innere oder äußere HTML des Zielelements, was die tatsächliche DOM-Ausgabe besser widerspiegelt.

enzyme

import { configure, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

test('should match snapshot', () => {
   const component = mount(<DialogComponent />);
   expect(component.html()).toMatchSnapshot();
 });

testing-library/react

import { render } from '@testing-library/react';

test('should match snapshot', () => {
   const { container } = render(<DialogComponent />);
   expect(container.innerHTML.toMatchSnapshot();
 });

Abfrage-Selektoren

react-testing-library bietet eine Auswahl an Abfragemethoden, um Ziel-DOM-Knoten zu finden. getByTestId ist besonders nützlich, da es das Auffinden von Ziel-Tags durch die Abfrage ihrer eindeutigen Test-ID präzisiert.

enzyme

import { configure, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

test('should close Dialog on close button click.', () => {
   const wrapper = mount(<ToggleButton />);
   const component = wrapper.find('ToggleButton');
   expect(component).toBeTruthy();
 });

testing-library/react

import { render } from '@testing-library/react';

test('should render toggle button', () => {
   const { getByTestId } = render(<ToggleButton />);
   const toggleBtn = getByTestId('toggle-btn');
   expect(toggleBtn).toBeTruthy();
 });

Events

Eine weitere praktische Funktion in testing-library/react ist die unkomplizierte Aktualisierung von Komponenten innerhalb von Tests mit async - await. Dies macht es sehr einfach, auf Änderungen nach Ereignissen wie Klicks oder Eingabeänderungen zu warten.

enzyme

import { configure, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

configure({ adapter: new Adapter() });

test("set state's text property on input change", () => {
   const text = 'Some text';
   const wrapper = mount(<TextInput value={text} />);
   expect(wrapper.find('TextInput').state().name)
     .toEqual(text);

   wrapper.find('textarea')
     .simulate('change', { target: { value: 'bar' } });

   expect(wrapper.find('TextInput').state().name)
     .toEqual('bar');
 });

testing-library/react

import { render, screen, fireEvent } from '@testing-library/react';

test("set state's text property on input change", async () => {
   const text = 'Some text';
   render(<TextInput value={text} />);
   const textarea = screen.getByTestId('text-area')
       .querySelector('textarea');
   expect(textarea.value).toBe(text);

   await fireEvent.change(textarea, { target: { value: 'bar' } });
   expect(textarea.value).toBe('bar');
});
geschrieben von Daniel Marsh-Hunn | 4.7.2022
Mehr zum Thema

Kontakt

geOps AG
Solothurnerstrasse 235
CH-4600 Olten

fon: +41 61 588 05 05
mail: info@geops.ch
geOps GmbH
Bismarckallee 10
D-79098 Freiburg im Breisgau

fon: +49 761 458 925 0
mail: info@geops.de
Impressum | Datenschutz | Bedingungen