Meistern Sie das Testen von React-Komponenten mit der React Testing Library. Lernen Sie Best Practices für wartbare, effektive Tests, die sich auf Nutzerverhalten und Barrierefreiheit konzentrieren.
React Testing Library: Best Practices für Komponententests für globale Teams
In der sich ständig weiterentwickelnden Welt der Webentwicklung ist die Gewährleistung der Zuverlässigkeit und Qualität Ihrer React-Anwendungen von größter Bedeutung. Dies gilt insbesondere für globale Teams, die an Projekten mit vielfältigen Nutzergruppen und Anforderungen an die Barrierefreiheit arbeiten. Die React Testing Library (RTL) bietet einen leistungsstarken und benutzerzentrierten Ansatz für das Testen von Komponenten. Im Gegensatz zu herkömmlichen Testmethoden, die sich auf Implementierungsdetails konzentrieren, ermutigt RTL Sie, Ihre Komponenten so zu testen, wie ein Benutzer mit ihnen interagieren würde, was zu robusteren und wartbareren Tests führt. Dieser umfassende Leitfaden wird auf die besten Praktiken für die Nutzung von RTL in Ihren React-Projekten eingehen, mit einem Fokus auf die Erstellung von Anwendungen, die für ein globales Publikum geeignet sind.
Warum React Testing Library?
Bevor wir uns mit den Best Practices befassen, ist es wichtig zu verstehen, warum sich RTL von anderen Testbibliotheken abhebt. Hier sind einige wichtige Vorteile:
- Benutzerzentrierter Ansatz: RTL priorisiert das Testen von Komponenten aus der Perspektive des Benutzers. Sie interagieren mit der Komponente mit denselben Methoden, die auch ein Benutzer anwenden würde (z.B. Klicken von Schaltflächen, Eingabe in Textfelder), was ein realistischeres und zuverlässigeres Testerlebnis gewährleistet.
- Fokus auf Barrierefreiheit: RTL fördert das Schreiben barrierefreier Komponenten, indem es Sie dazu anregt, diese so zu testen, dass Benutzer mit Behinderungen berücksichtigt werden. Dies steht im Einklang mit globalen Barrierefreiheitsstandards wie WCAG.
- Reduzierter Wartungsaufwand: Indem das Testen von Implementierungsdetails (z.B. interner Zustand, spezifische Funktionsaufrufe) vermieden wird, ist es weniger wahrscheinlich, dass RTL-Tests bei einer Code-Umstrukturierung (Refactoring) fehlschlagen. Dies führt zu wartbareren und widerstandsfähigeren Tests.
- Verbessertes Code-Design: Der benutzerzentrierte Ansatz von RTL führt oft zu einem besseren Komponentendesign, da Sie gezwungen sind, darüber nachzudenken, wie Benutzer mit Ihren Komponenten interagieren werden.
- Community und Ökosystem: RTL verfügt über eine große und aktive Community, die zahlreiche Ressourcen, Unterstützung und Erweiterungen bietet.
Einrichten Ihrer Testumgebung
Um mit RTL zu beginnen, müssen Sie Ihre Testumgebung einrichten. Hier ist eine grundlegende Einrichtung mit Create React App (CRA), die bereits mit Jest und RTL vorkonfiguriert ist:
npx create-react-app my-react-app
cd my-react-app
npm install --save-dev @testing-library/react @testing-library/jest-dom
Erklärung:
- `npx create-react-app my-react-app`: Erstellt ein neues React-Projekt mit Create React App.
- `cd my-react-app`: Navigiert in das neu erstellte Projektverzeichnis.
- `npm install --save-dev @testing-library/react @testing-library/jest-dom`: Installiert die notwendigen RTL-Pakete als Entwicklungsabhängigkeiten. `@testing-library/react` stellt die Kernfunktionalität von RTL bereit, während `@testing-library/jest-dom` hilfreiche Jest-Matcher für die Arbeit mit dem DOM bietet.
Wenn Sie CRA nicht verwenden, müssen Sie Jest und RTL separat installieren und Jest für die Verwendung mit RTL konfigurieren.
Best Practices für Komponententests mit der React Testing Library
1. Schreiben Sie Tests, die Benutzerinteraktionen nachahmen
Das Kernprinzip von RTL besteht darin, Komponenten so zu testen, wie es ein Benutzer tun würde. Das bedeutet, sich auf das zu konzentrieren, was der Benutzer sieht und tut, anstatt auf interne Implementierungsdetails. Verwenden Sie das von RTL bereitgestellte `screen`-Objekt, um Elemente anhand ihres Textes, ihrer Rolle oder ihrer Barrierefreiheits-Labels abzufragen.
Beispiel: Testen eines Button-Klicks
Angenommen, Sie haben eine einfache Button-Komponente:
// Button.js
import React from 'react';
function Button({ onClick, children }) {
return ;
}
export default Button;
So würden Sie sie mit RTL testen:
// Button.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
describe('Button Component', () => {
it('calls the onClick handler when clicked', () => {
const handleClick = jest.fn();
render();
const buttonElement = screen.getByText('Click Me');
fireEvent.click(buttonElement);
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
Erklärung:
- `render()`: Rendert die Button-Komponente mit einem simulierten `onClick`-Handler.
- `screen.getByText('Click Me')`: Frägt das Dokument nach einem Element ab, das den Text „Click Me“ enthält. So würde ein Benutzer die Schaltfläche identifizieren.
- `fireEvent.click(buttonElement)`: Simuliert ein Klick-Ereignis auf dem Button-Element.
- `expect(handleClick).toHaveBeenCalledTimes(1)`: Stellt sicher, dass der `onClick`-Handler einmal aufgerufen wurde.
Warum dies besser ist als das Testen von Implementierungsdetails: Stellen Sie sich vor, Sie strukturieren die Button-Komponente um, um einen anderen Event-Handler zu verwenden oder den internen Zustand zu ändern. Wenn Sie die spezifische Event-Handler-Funktion testen würden, würde Ihr Test fehlschlagen. Indem Sie sich auf die Benutzerinteraktion (das Klicken des Buttons) konzentrieren, bleibt der Test auch nach dem Refactoring gültig.
2. Priorisieren Sie Abfragen basierend auf der Benutzerabsicht
RTL bietet verschiedene Abfragemethoden zum Finden von Elementen. Priorisieren Sie die folgenden Abfragen in dieser Reihenfolge, da sie am besten widerspiegeln, wie Benutzer Ihre Komponenten wahrnehmen und mit ihnen interagieren:
- getByRole: Diese Abfrage ist die zugänglichste und sollte Ihre erste Wahl sein. Sie ermöglicht es Ihnen, Elemente basierend auf ihren ARIA-Rollen (z.B. button, link, heading) zu finden.
- getByLabelText: Verwenden Sie dies, um Elemente zu finden, die mit einem bestimmten Label verknüpft sind, wie z.B. Eingabefelder.
- getByPlaceholderText: Verwenden Sie dies, um Eingabefelder anhand ihres Platzhaltertextes zu finden.
- getByText: Verwenden Sie dies, um Elemente anhand ihres Textinhalts zu finden. Seien Sie spezifisch und vermeiden Sie generischen Text, der an mehreren Stellen vorkommen könnte.
- getByDisplayValue: Verwenden Sie dies, um Eingabefelder anhand ihres aktuellen Wertes zu finden.
Beispiel: Testen einer Formulareingabe
// Input.js
import React from 'react';
function Input({ label, placeholder, value, onChange }) {
return (
);
}
export default Input;
So testen Sie es unter Verwendung der empfohlenen Abfragereihenfolge:
// Input.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Input from './Input';
describe('Input Component', () => {
it('updates the value when the user types', () => {
const handleChange = jest.fn();
render();
const inputElement = screen.getByLabelText('Name');
fireEvent.change(inputElement, { target: { value: 'John Doe' } });
expect(handleChange).toHaveBeenCalledTimes(1);
expect(handleChange).toHaveBeenCalledWith(expect.objectContaining({ target: { value: 'John Doe' } }));
});
});
Erklärung:
- `screen.getByLabelText('Name')`: Verwendet `getByLabelText`, um das Eingabefeld zu finden, das mit dem Label „Name“ verknüpft ist. Dies ist die zugänglichste und benutzerfreundlichste Methode, um das Eingabefeld zu lokalisieren.
3. Vermeiden Sie das Testen von Implementierungsdetails
Wie bereits erwähnt, vermeiden Sie das Testen des internen Zustands, von Funktionsaufrufen oder spezifischen CSS-Klassen. Dies sind Implementierungsdetails, die sich ändern können und zu fragilen Tests führen. Konzentrieren Sie sich auf das beobachtbare Verhalten der Komponente.
Beispiel: Vermeiden Sie das direkte Testen des Zustands
Anstatt zu testen, ob eine bestimmte Zustandsvariable aktualisiert wird, testen Sie, ob die Komponente die korrekte Ausgabe basierend auf diesem Zustand rendert. Wenn eine Komponente beispielsweise eine Nachricht basierend auf einer booleschen Zustandsvariable anzeigt, testen Sie, ob die Nachricht angezeigt oder ausgeblendet wird, anstatt die Zustandsvariable selbst zu testen.
4. Verwenden Sie `data-testid` für spezielle Fälle
Obwohl es im Allgemeinen am besten ist, die Verwendung von `data-testid`-Attributen zu vermeiden, gibt es spezielle Fälle, in denen sie hilfreich sein können:
- Elemente ohne semantische Bedeutung: Wenn Sie ein Element ansprechen müssen, das keine aussagekräftige Rolle, kein Label oder keinen Text hat, können Sie `data-testid` verwenden.
- Komplexe Komponentenstrukturen: In komplexen Komponentenstrukturen kann `data-testid` Ihnen helfen, spezifische Elemente anzusprechen, ohne sich auf fragile Selektoren verlassen zu müssen.
- Barrierefreiheitstests: `data-testid` kann zur Identifizierung spezifischer Elemente bei Barrierefreiheitstests mit Tools wie Cypress oder Playwright verwendet werden.
Beispiel: Verwendung von `data-testid`
// MyComponent.js
import React from 'react';
function MyComponent() {
return (
This is my component.
);
}
export default MyComponent;
// MyComponent.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
describe('MyComponent', () => {
it('renders the component container', () => {
render( );
const containerElement = screen.getByTestId('my-component-container');
expect(containerElement).toBeInTheDocument();
});
});
Wichtig: Verwenden Sie `data-testid` sparsam und nur dann, wenn andere Abfragemethoden nicht geeignet sind.
5. Schreiben Sie aussagekräftige Testbeschreibungen
Klare und prägnante Testbeschreibungen sind entscheidend für das Verständnis des Zwecks jedes Tests und für die Fehlersuche. Verwenden Sie beschreibende Namen, die klar erklären, was der Test überprüft.
Beispiel: Gute vs. schlechte Testbeschreibungen
Schlecht: `it('funktioniert')`
Gut: `it('zeigt die korrekte Begrüßungsnachricht an')`
Noch besser: `it('zeigt die Begrüßungsnachricht \"Hallo, Welt!\" an, wenn die name-Prop nicht bereitgestellt wird')`
Das bessere Beispiel beschreibt klar das erwartete Verhalten der Komponente unter bestimmten Bedingungen.
6. Halten Sie Ihre Tests klein und fokussiert
Jeder Test sollte sich darauf konzentrieren, einen einzelnen Aspekt des Verhaltens der Komponente zu überprüfen. Vermeiden Sie das Schreiben großer, komplexer Tests, die mehrere Szenarien abdecken. Kleine, fokussierte Tests sind leichter zu verstehen, zu warten und zu debuggen.
7. Verwenden Sie Test-Doubles (Mocks und Spies) angemessen
Test-Doubles sind nützlich, um die zu testende Komponente von ihren Abhängigkeiten zu isolieren. Verwenden Sie Mocks und Spies, um externe Dienste, API-Aufrufe oder andere Komponenten zu simulieren.
Beispiel: Mocking eines API-Aufrufs
// UserList.js
import React, { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
async function fetchUsers() {
const response = await fetch('/api/users');
const data = await response.json();
setUsers(data);
}
fetchUsers();
}, []);
return (
{users.map(user => (
- {user.name}
))}
);
}
export default UserList;
// UserList.test.js
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import UserList from './UserList';
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve([
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' },
]),
})
);
describe('UserList Component', () => {
it('fetches and displays a list of users', async () => {
render( );
// Wait for the data to load
await waitFor(() => screen.getByText('John Doe'));
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('Jane Smith')).toBeInTheDocument();
});
});
Erklärung:
- `global.fetch = jest.fn(...)`: Mockt die `fetch`-Funktion, um eine vordefinierte Liste von Benutzern zurückzugeben. Dies ermöglicht es Ihnen, die Komponente zu testen, ohne auf einen echten API-Endpunkt angewiesen zu sein.
- `await waitFor(() => screen.getByText('John Doe'))`: Wartet, bis der Text „John Doe“ im Dokument erscheint. Dies ist notwendig, da die Daten asynchron abgerufen werden.
8. Testen Sie Grenzfälle und Fehlerbehandlung
Testen Sie nicht nur den „Happy Path“. Stellen Sie sicher, dass Sie auch Grenzfälle, Fehlerszenarien und Randbedingungen testen. Dies hilft Ihnen, potenzielle Probleme frühzeitig zu erkennen und sicherzustellen, dass Ihre Komponente unerwartete Situationen elegant handhabt.
Beispiel: Testen der Fehlerbehandlung
Stellen Sie sich eine Komponente vor, die Daten von einer API abruft und eine Fehlermeldung anzeigt, wenn der API-Aufruf fehlschlägt. Sie sollten einen Test schreiben, um zu überprüfen, dass die Fehlermeldung korrekt angezeigt wird, wenn der API-Aufruf fehlschlägt.
9. Konzentrieren Sie sich auf Barrierefreiheit
Barrierefreiheit ist entscheidend für die Erstellung inklusiver Webanwendungen. Verwenden Sie RTL, um die Barrierefreiheit Ihrer Komponenten zu testen und sicherzustellen, dass sie Barrierefreiheitsstandards wie WCAG erfüllen. Einige wichtige Überlegungen zur Barrierefreiheit sind:
- Semantisches HTML: Verwenden Sie semantische HTML-Elemente (z.B. `
- ARIA-Attribute: Verwenden Sie ARIA-Attribute, um zusätzliche Informationen über die Rolle, den Zustand und die Eigenschaften von Elementen bereitzustellen, insbesondere für benutzerdefinierte Komponenten.
- Tastaturnavigation: Stellen Sie sicher, dass alle interaktiven Elemente über die Tastatur zugänglich sind.
- Farbkontrast: Verwenden Sie einen ausreichenden Farbkontrast, um sicherzustellen, dass Text für Benutzer mit Sehschwäche lesbar ist.
- Screenreader-Kompatibilität: Testen Sie Ihre Komponenten mit einem Screenreader, um sicherzustellen, dass sie eine sinnvolle und verständliche Erfahrung für Benutzer mit Sehbehinderungen bieten.
Beispiel: Testen der Barrierefreiheit mit `getByRole`
// MyAccessibleComponent.js
import React from 'react';
function MyAccessibleComponent() {
return (
);
}
export default MyAccessibleComponent;
// MyAccessibleComponent.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import MyAccessibleComponent from './MyAccessibleComponent';
describe('MyAccessibleComponent', () => {
it('renders an accessible button with the correct aria-label', () => {
render( );
const buttonElement = screen.getByRole('button', { name: 'Close' });
expect(buttonElement).toBeInTheDocument();
});
});
Erklärung:
- `screen.getByRole('button', { name: 'Close' })`: Verwendet `getByRole`, um ein Button-Element mit dem zugänglichen Namen „Close“ zu finden. Dies stellt sicher, dass die Schaltfläche für Screenreader korrekt beschriftet ist.
10. Integrieren Sie Tests in Ihren Entwicklungsworkflow
Tests sollten ein integraler Bestandteil Ihres Entwicklungsworkflows sein, kein nachträglicher Gedanke. Integrieren Sie Ihre Tests in Ihre CI/CD-Pipeline, um Tests automatisch auszuführen, wann immer Code committet oder bereitgestellt wird. Dies hilft Ihnen, Fehler frühzeitig zu erkennen und Regressionen zu vermeiden.
11. Berücksichtigen Sie Lokalisierung und Internationalisierung (i18n)
Für globale Anwendungen ist es entscheidend, bei den Tests die Lokalisierung und Internationalisierung (i18n) zu berücksichtigen. Stellen Sie sicher, dass Ihre Komponenten in verschiedenen Sprachen und Gebietsschemata korrekt gerendert werden.
Beispiel: Testen der Lokalisierung
Wenn Sie eine Bibliothek wie `react-intl` oder `i18next` für die Lokalisierung verwenden, können Sie den Lokalisierungskontext in Ihren Tests mocken, um zu überprüfen, ob Ihre Komponenten den korrekten übersetzten Text anzeigen.
12. Verwenden Sie benutzerdefinierte Render-Funktionen für wiederverwendbare Setups
Bei der Arbeit an größeren Projekten werden Sie möglicherweise feststellen, dass Sie dieselben Einrichtungsschritte in mehreren Tests wiederholen. Um Duplizierung zu vermeiden, erstellen Sie benutzerdefinierte Render-Funktionen, die die gemeinsame Einrichtungslogik kapseln.
Beispiel: Benutzerdefinierte Render-Funktion
// test-utils.js
import React from 'react';
import { render } from '@testing-library/react';
import { ThemeProvider } from 'styled-components';
import theme from './theme';
const AllTheProviders = ({ children }) => {
return (
{children}
);
}
const customRender = (ui, options) =>
render(ui, { wrapper: AllTheProviders, ...options })
// re-export everything
export * from '@testing-library/react'
// override render method
export { customRender as render }
// MyComponent.test.js
import React from 'react';
import { render, screen } from './test-utils'; // Import the custom render
import MyComponent from './MyComponent';
describe('MyComponent', () => {
it('renders correctly with the theme', () => {
render( );
// Your test logic here
});
});
Dieses Beispiel erstellt eine benutzerdefinierte Render-Funktion, die die Komponente mit einem ThemeProvider umschließt. Dies ermöglicht es Ihnen, Komponenten, die vom Theme abhängen, einfach zu testen, ohne die ThemeProvider-Einrichtung in jedem Test wiederholen zu müssen.
Fazit
Die React Testing Library bietet einen leistungsstarken und benutzerzentrierten Ansatz für das Testen von Komponenten. Indem Sie diese Best Practices befolgen, können Sie wartbare, effektive Tests schreiben, die sich auf das Benutzerverhalten und die Barrierefreiheit konzentrieren. Dies führt zu robusteren, zuverlässigeren und inklusiveren React-Anwendungen für ein globales Publikum. Denken Sie daran, Benutzerinteraktionen zu priorisieren, das Testen von Implementierungsdetails zu vermeiden, sich auf die Barrierefreiheit zu konzentrieren und Tests in Ihren Entwicklungsworkflow zu integrieren. Indem Sie diese Prinzipien beherzigen, können Sie hochwertige React-Anwendungen erstellen, die den Bedürfnissen von Benutzern auf der ganzen Welt gerecht werden.
Wichtige Erkenntnisse:
- Fokus auf Benutzerinteraktionen: Testen Sie Komponenten so, wie ein Benutzer mit ihnen interagieren würde.
- Priorisieren Sie Barrierefreiheit: Stellen Sie sicher, dass Ihre Komponenten für Benutzer mit Behinderungen zugänglich sind.
- Vermeiden Sie Implementierungsdetails: Testen Sie nicht den internen Zustand oder Funktionsaufrufe.
- Schreiben Sie klare und prägnante Tests: Machen Sie Ihre Tests leicht verständlich und wartbar.
- Integrieren Sie Tests in Ihren Workflow: Automatisieren Sie Ihre Tests und führen Sie sie regelmäßig aus.
- Berücksichtigen Sie globale Zielgruppen: Stellen Sie sicher, dass Ihre Komponenten in verschiedenen Sprachen und Gebietsschemata gut funktionieren.