Back

Wie man das DOM in React Testing Library abfragt

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)
Abfragetyp0 Treffer1 Treffer>1 TrefferAsynchron
getByFehler werfenElement zurückgebenFehler werfenNein
queryBynull zurückgebenElement zurückgebenFehler werfenNein
findByFehler werfenElement zurückgebenFehler werfenJa

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.

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers