Wie man das DOM in React Testing Library abfragt

React Testing Library DOM-Abfragen sind das Fundament effektiver Komponententests, dennoch haben viele Entwickler Schwierigkeiten bei der Auswahl der richtigen Abfragemethode für ihre spezifischen Testszenarien. Ob Sie es mit synchronen Elementen, asynchronen Komponenten oder bedingtem Rendering zu tun haben – das Verständnis dafür, wann getBy-, findBy- und queryBy-Methoden verwendet werden sollten, kann den Unterschied zwischen zuverlässigen Tests und instabilen Tests ausmachen.
Dieser Leitfaden behandelt die wesentlichen React Testing Library Abfragemethoden, ihre Verhaltensunterschiede und praktische Beispiele, um Ihnen dabei zu helfen, robustere Tests zu schreiben. Sie lernen Richtlinien für die Abfragepriorität, häufige Fallstricke, die es zu vermeiden gilt, und praxisnahe Testmuster kennen, die die Testzuverlässigkeit verbessern.
Wichtige Erkenntnisse
- Verwenden Sie getBy-Abfragen für Elemente, die unmittelbar nach dem Rendering vorhanden sein sollten
- Verwenden Sie findBy-Abfragen für Elemente, die asynchron erscheinen, beispielsweise nach API-Aufrufen oder Zustandsänderungen
- Verwenden Sie queryBy-Abfragen beim Testen, dass Elemente nicht im DOM vorhanden sind
- Priorisieren Sie getByRole- und getByLabelText-Abfragen für bessere Barrierefreiheitstests
- Reservieren Sie getByTestId für komplexe Szenarien, in denen semantische Abfragen nicht praktikabel sind
- Debuggen Sie fehlgeschlagene Abfragen mit screen.debug() und Testing Playground für eine bessere Abfrageauswahl
Erste Schritte mit React Testing Library DOM-Abfragen
React Testing Library baut auf DOM Testing Library auf, indem es APIs hinzufügt, die speziell für das Testen von React-Komponenten entwickelt wurden. Installieren Sie es in Ihrem React-Projekt:
npm install --save-dev @testing-library/react @testing-library/jest-dom
React Testing Library DOM-Abfragen funktionieren, indem sie Elemente im DOM-Baum der gerenderten Komponente finden, ähnlich wie Benutzer mit Ihrer Anwendung interagieren. Hier ist ein grundlegendes Beispiel:
import { render, screen } from '@testing-library/react'
import LoginForm from './LoginForm'
test('renders login form', () => {
render(<LoginForm />)
const usernameInput = screen.getByLabelText('Username')
expect(usernameInput).toBeInTheDocument()
})
Der wesentliche Unterschied zur DOM Testing Library besteht darin, dass React Testing Library automatisch React-spezifische Belange wie Komponenten-Rendering, Zustandsänderungen und Aufräumarbeiten behandelt.
Verständnis der React Testing Library Abfragetypen
React Testing Library bietet drei Hauptabfragetypen, die jeweils unterschiedliche Verhaltensweisen für den Umgang mit fehlenden Elementen und Timing aufweisen.
getBy-Abfragen - Synchrone Elementauswahl
getBy-Abfragen geben Elemente sofort zurück und werfen Fehler, wenn Elemente nicht gefunden werden oder mehrere Übereinstimmungen existieren:
// Einzelnes Element - wirft Fehler wenn nicht gefunden oder mehrere gefunden
const button = screen.getByRole('button', { name: 'Submit' })
// Mehrere Elemente - wirft Fehler wenn keine gefunden
const listItems = screen.getAllByRole('listitem')
Verwenden Sie getBy-Abfragen, wenn Sie erwarten, dass Elemente unmittelbar nach dem Rendering im DOM vorhanden sind.
findBy-Abfragen - Asynchrone DOM-Abfragen
findBy-Abfragen geben Promises zurück und wiederholen Versuche, bis Elemente erscheinen oder ein Timeout auftritt (Standard 1000ms):
// Warten auf asynchrones Element
const successMessage = await screen.findByText('Profile updated successfully')
// Mehrere asynchrone Elemente
const loadedItems = await screen.findAllByTestId('product-card')
Verwenden Sie findBy-Abfragen für Elemente, die nach API-Aufrufen, Zustandsänderungen oder anderen asynchronen Operationen erscheinen.
queryBy-Abfragen - Bedingte Elementtests
queryBy-Abfragen geben null zurück, wenn Elemente nicht gefunden werden, was sie perfekt für das Testen der Abwesenheit von Elementen macht:
// Testen, dass Element nicht existiert
const errorMessage = screen.queryByText('Error occurred')
expect(errorMessage).not.toBeInTheDocument()
// Mehrere Elemente - gibt leeres Array zurück wenn keine gefunden
const hiddenElements = screen.queryAllByTestId('hidden-item')
expect(hiddenElements).toHaveLength(0)
Abfragetyp | 0 Treffer | 1 Treffer | >1 Treffer | Asynchron |
---|---|---|---|---|
getBy | Fehler werfen | Element zurückgeben | Fehler werfen | Nein |
queryBy | null zurückgeben | Element zurückgeben | Fehler werfen | Nein |
findBy | Fehler werfen | Element zurückgeben | Fehler werfen | Ja |
React Testing Library Abfragepriorität-Leitfaden
React Testing Library ermutigt dazu, Komponenten so zu testen, wie Benutzer mit ihnen interagieren. Dieser Prioritätsleitfaden hilft Ihnen bei der Auswahl von Abfragen, die echtes Benutzerverhalten widerspiegeln.
Barrierefreiheit-orientierte Abfragen (getByRole, getByLabelText)
getByRole sollte Ihre erste Wahl für die meisten Elemente sein:
// Buttons, Links, Formularsteuerelemente
const submitButton = screen.getByRole('button', { name: 'Create Account' })
const navigationLink = screen.getByRole('link', { name: 'About Us' })
// Überschriften mit spezifischen Ebenen
const pageTitle = screen.getByRole('heading', { level: 1 })
getByLabelText funktioniert am besten für Formularfelder:
const emailInput = screen.getByLabelText('Email Address')
const passwordInput = screen.getByLabelText(/password/i)
Diese Abfragen stellen sicher, dass Ihre Komponenten mit Hilfstechnologien funktionieren.
Inhaltsbasierte Abfragen (getByText, getByPlaceholderText)
getByText findet Elemente anhand ihres sichtbaren Textinhalts:
// Exakte Textübereinstimmung
const welcomeMessage = screen.getByText('Welcome back, John!')
// Regex für flexible Übereinstimmung
const errorText = screen.getByText(/something went wrong/i)
getByPlaceholderText hilft, wenn Labels nicht verfügbar sind:
const searchInput = screen.getByPlaceholderText('Search products...')
Wann getByTestId verwendet werden sollte
Reservieren Sie getByTestId für Fälle, in denen semantische Abfragen nicht ausreichen:
// Dynamischer Inhalt, bei dem sich Text ändert
const userAvatar = screen.getByTestId('user-avatar')
// Komplexe Komponenten ohne klare Rollen
const chartContainer = screen.getByTestId('sales-chart')
Fügen Sie Test-IDs sparsam hinzu und bevorzugen Sie semantische Abfragen, wann immer möglich.
Häufige React Testing Library DOM-Abfrage Fallstricke
Vermeidung instabiler Tests durch richtige Abfrageauswahl
Problem: Verwendung von getBy-Abfragen für Elemente, die asynchron geladen werden:
// ❌ Instabil - Element könnte noch nicht geladen sein
test('shows user profile', () => {
render(<UserProfile userId="123" />)
const userName = screen.getByText('John Doe') // Kann fehlschlagen
})
Lösung: Verwenden Sie findBy für asynchrone Elemente:
// ✅ Zuverlässig - wartet darauf, dass Element erscheint
test('shows user profile', async () => {
render(<UserProfile userId="123" />)
const userName = await screen.findByText('John Doe')
expect(userName).toBeInTheDocument()
})
Debugging fehlgeschlagener Abfragen
Wenn Abfragen fehlschlagen, bietet React Testing Library hilfreiche Debugging-Tools:
// Sehen Sie, was tatsächlich im DOM ist
screen.debug()
// Erhalten Sie Vorschläge für bessere Abfragen
screen.getByRole('button') // Fehlermeldung schlägt verfügbare Rollen vor
Verwenden Sie Testing Playground, um mit Abfragen auf Ihrem tatsächlichen HTML zu experimentieren.
Übermäßige Abhängigkeit von Test-IDs
Problem: Verwendung von getByTestId als Standard-Abfragemethode:
// ❌ Nicht benutzerfokussiert
const button = screen.getByTestId('submit-button')
Lösung: Verwenden Sie semantische Abfragen, die Benutzerinteraktion widerspiegeln:
// ✅ Benutzerfokussiert
const button = screen.getByRole('button', { name: 'Submit Form' })
Test-IDs sollten Ihr letzter Ausweg sein, nicht Ihre erste Wahl.
Praxisnahe React Testing Library Beispiele
Hier sind praktische Beispiele, die verschiedene Abfragemethoden in Aktion zeigen:
Formular-Tests:
import { render, screen, fireEvent } from '@testing-library/react'
test('handles form submission', async () => {
render(<ContactForm />)
// Verwenden Sie getByLabelText für Formularfelder
const nameInput = screen.getByLabelText('Full Name')
const emailInput = screen.getByLabelText('Email')
const submitButton = screen.getByRole('button', { name: 'Send Message' })
// Formular ausfüllen und absenden
fireEvent.change(nameInput, { target: { value: 'John Doe' } })
fireEvent.change(emailInput, { target: { value: 'john@example.com' } })
fireEvent.click(submitButton)
// Auf Erfolgsmeldung warten
const successMessage = await screen.findByText('Message sent successfully!')
expect(successMessage).toBeInTheDocument()
})
Fehlerzustand-Tests:
import { rest } from 'msw'
test('displays error when API fails', async () => {
// API-Fehler mocken
server.use(
rest.get('/api/users', (req, res, ctx) => {
return res(ctx.status(500))
})
)
render(<UserList />)
// Warten darauf, dass Fehlermeldung erscheint
const errorMessage = await screen.findByText(/failed to load users/i)
expect(errorMessage).toBeInTheDocument()
// Überprüfen, dass Ladezustand verschwunden ist
const loadingSpinner = screen.queryByTestId('loading-spinner')
expect(loadingSpinner).not.toBeInTheDocument()
})
Fazit
Die Beherrschung von React Testing Library DOM-Abfragen erfordert das Verständnis dafür, wann getBy für unmittelbare Elemente, findBy für asynchrone Inhalte und queryBy für das Testen der Abwesenheit von Elementen verwendet werden sollte. Priorisieren Sie barrierefreiheitsorientierte Abfragen wie getByRole und getByLabelText, und reservieren Sie getByTestId für Grenzfälle, in denen semantische Abfragen nicht ausreichen.
Der Schlüssel zu zuverlässigen Tests liegt in der Auswahl von Abfragen, die widerspiegeln, wie Benutzer mit Ihren Komponenten interagieren. Dieser Ansatz erstellt Tests, die sowohl robust als auch wartbar sind, echte benutzerseitige Probleme erfassen und gleichzeitig widerstandsfähig gegen Implementierungsänderungen bleiben.
Häufig gestellte Fragen
Verwenden Sie findBy-Abfragen beim Testen von Elementen, die nach asynchronen Operationen wie API-Aufrufen, setTimeout oder Zustandsänderungen erscheinen. findBy-Abfragen wiederholen automatisch Versuche, bis das Element erscheint oder das Timeout erreicht wird, wodurch instabile Tests verhindert werden.
getByTestId-Abfragen spiegeln nicht wider, wie Benutzer mit Ihrer Anwendung interagieren. Benutzer sehen keine Test-IDs - sie interagieren mit Buttons, lesen Text und verwenden Formularlabels. Semantische Abfragen wie getByRole und getByText erstellen realistischere und wartbarere Tests.
Verwenden Sie queryBy-Abfragen, die null zurückgeben, wenn Elemente nicht gefunden werden, anstatt Fehler zu werfen. Zum Beispiel: expect(screen.queryByText('Error message')).not.toBeInTheDocument().
getAllBy-Abfragen geben sofort Arrays von Elementen zurück und werfen Fehler, wenn keine Elemente gefunden werden. findAllBy-Abfragen geben Promises zurück, die zu Arrays aufgelöst werden, und warten darauf, dass mindestens ein Element erscheint, bevor sie aufgelöst werden.
Verwenden Sie screen.debug(), um die aktuelle DOM-Struktur zu sehen, überprüfen Sie die Fehlermeldungen auf Abfragevorschläge und probieren Sie Testing Playground aus, um mit verschiedenen Abfrageansätzen auf Ihrem tatsächlichen HTML zu experimentieren.