Como Consultar o DOM no React Testing Library

As consultas DOM do React Testing Library são a base de testes de componentes eficazes, mas muitos desenvolvedores têm dificuldade em escolher o método de consulta correto para seus cenários específicos de teste. Seja lidando com elementos síncronos, componentes assíncronos ou renderização condicional, entender quando usar os métodos getBy, findBy e queryBy pode fazer a diferença entre testes confiáveis e instáveis.
Este guia aborda os métodos essenciais de consulta do React Testing Library, suas diferenças comportamentais e exemplos práticos para ajudá-lo a escrever testes mais robustos. Você aprenderá diretrizes de prioridade de consultas, armadilhas comuns a evitar e padrões de teste do mundo real que melhoram a confiabilidade dos testes.
Principais Pontos
- Use consultas getBy para elementos que devem estar imediatamente presentes após a renderização
- Use consultas findBy para elementos que aparecem de forma assíncrona, como após chamadas de API ou atualizações de estado
- Use consultas queryBy ao testar que elementos não estão presentes no DOM
- Priorize consultas getByRole e getByLabelText para melhor teste de acessibilidade
- Reserve getByTestId para cenários complexos onde consultas semânticas não são práticas
- Depure consultas que falharam usando screen.debug() e Testing Playground para melhor seleção de consultas
Começando com Consultas DOM do React Testing Library
O React Testing Library é construído sobre a DOM Testing Library adicionando APIs especificamente projetadas para testar componentes React. Instale-o em seu projeto React:
npm install --save-dev @testing-library/react @testing-library/jest-dom
As consultas DOM do React Testing Library funcionam encontrando elementos na árvore DOM do componente renderizado, similar a como os usuários interagem com sua aplicação. Aqui está um exemplo básico:
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()
})
A principal diferença da DOM Testing Library é que o React Testing Library automaticamente lida com preocupações específicas do React como renderização de componentes, atualizações de estado e limpeza.
Entendendo os Tipos de Consulta do React Testing Library
O React Testing Library fornece três tipos principais de consulta, cada um com comportamentos diferentes para lidar com elementos ausentes e temporização.
Consultas getBy - Seleção Síncrona de Elementos
As consultas getBy retornam elementos imediatamente e lançam erros se elementos não forem encontrados ou se múltiplas correspondências existirem:
// Elemento único - lança erro se não encontrado ou múltiplos encontrados
const button = screen.getByRole('button', { name: 'Submit' })
// Múltiplos elementos - lança erro se nenhum encontrado
const listItems = screen.getAllByRole('listitem')
Use consultas getBy quando você espera que elementos estejam presentes no DOM imediatamente após a renderização.
Consultas findBy - Consultas DOM Assíncronas
As consultas findBy retornam promises e tentam novamente até que elementos apareçam ou o timeout ocorra (padrão 1000ms):
// Aguarda elemento assíncrono aparecer
const successMessage = await screen.findByText('Profile updated successfully')
// Múltiplos elementos assíncronos
const loadedItems = await screen.findAllByTestId('product-card')
Use consultas findBy para elementos que aparecem após chamadas de API, atualizações de estado ou outras operações assíncronas.
Consultas queryBy - Teste Condicional de Elementos
As consultas queryBy retornam null quando elementos não são encontrados, tornando-as perfeitas para testar a ausência de elementos:
// Testa que elemento não existe
const errorMessage = screen.queryByText('Error occurred')
expect(errorMessage).not.toBeInTheDocument()
// Múltiplos elementos - retorna array vazio se nenhum encontrado
const hiddenElements = screen.queryAllByTestId('hidden-item')
expect(hiddenElements).toHaveLength(0)
Tipo de Consulta | 0 Correspondências | 1 Correspondência | >1 Correspondências | Assíncrono |
---|---|---|---|---|
getBy | Lança erro | Retorna elemento | Lança erro | Não |
queryBy | Retorna null | Retorna elemento | Lança erro | Não |
findBy | Lança erro | Retorna elemento | Lança erro | Sim |
Guia de Prioridade de Consultas do React Testing Library
O React Testing Library encoraja testar componentes como os usuários interagem com eles. Este guia de prioridade ajuda você a escolher consultas que refletem o comportamento real do usuário.
Consultas com Foco em Acessibilidade (getByRole, getByLabelText)
getByRole deve ser sua primeira escolha para a maioria dos elementos:
// Botões, links, controles de formulário
const submitButton = screen.getByRole('button', { name: 'Create Account' })
const navigationLink = screen.getByRole('link', { name: 'About Us' })
// Cabeçalhos com níveis específicos
const pageTitle = screen.getByRole('heading', { level: 1 })
getByLabelText funciona melhor para campos de formulário:
const emailInput = screen.getByLabelText('Email Address')
const passwordInput = screen.getByLabelText(/password/i)
Essas consultas garantem que seus componentes funcionem com tecnologias assistivas.
Consultas Baseadas em Conteúdo (getByText, getByPlaceholderText)
getByText encontra elementos pelo seu conteúdo de texto visível:
// Correspondência exata de texto
const welcomeMessage = screen.getByText('Welcome back, John!')
// Regex para correspondência flexível
const errorText = screen.getByText(/something went wrong/i)
getByPlaceholderText ajuda quando rótulos não estão disponíveis:
const searchInput = screen.getByPlaceholderText('Search products...')
Quando Usar getByTestId
Reserve getByTestId para casos onde consultas semânticas não são suficientes:
// Conteúdo dinâmico onde o texto muda
const userAvatar = screen.getByTestId('user-avatar')
// Componentes complexos sem papéis claros
const chartContainer = screen.getByTestId('sales-chart')
Adicione test IDs com moderação e prefira consultas semânticas sempre que possível.
Armadilhas Comuns das Consultas DOM do React Testing Library
Evitando Testes Instáveis com Seleção Adequada de Consultas
Problema: Usar consultas getBy para elementos que carregam de forma assíncrona:
// ❌ Instável - elemento pode não estar carregado ainda
test('shows user profile', () => {
render(<UserProfile userId="123" />)
const userName = screen.getByText('John Doe') // Pode falhar
})
Solução: Use findBy para elementos assíncronos:
// ✅ Confiável - aguarda elemento aparecer
test('shows user profile', async () => {
render(<UserProfile userId="123" />)
const userName = await screen.findByText('John Doe')
expect(userName).toBeInTheDocument()
})
Depurando Consultas que Falharam
Quando consultas falham, o React Testing Library fornece ferramentas úteis de depuração:
// Veja o que realmente está no DOM
screen.debug()
// Obtenha sugestões para melhores consultas
screen.getByRole('button') // Mensagem de erro sugere papéis disponíveis
Use o Testing Playground para experimentar com consultas no seu HTML real.
Dependência Excessiva de Test IDs
Problema: Usar getByTestId como método padrão de consulta:
// ❌ Não focado no usuário
const button = screen.getByTestId('submit-button')
Solução: Use consultas semânticas que refletem a interação do usuário:
// ✅ Focado no usuário
const button = screen.getByRole('button', { name: 'Submit Form' })
Test IDs devem ser seu último recurso, não sua primeira escolha.
Exemplos Práticos do React Testing Library
Aqui estão exemplos práticos mostrando diferentes métodos de consulta em ação:
Teste de Formulário:
import { render, screen, fireEvent } from '@testing-library/react'
test('handles form submission', async () => {
render(<ContactForm />)
// Use getByLabelText para campos de formulário
const nameInput = screen.getByLabelText('Full Name')
const emailInput = screen.getByLabelText('Email')
const submitButton = screen.getByRole('button', { name: 'Send Message' })
// Preenche formulário e submete
fireEvent.change(nameInput, { target: { value: 'John Doe' } })
fireEvent.change(emailInput, { target: { value: 'john@example.com' } })
fireEvent.click(submitButton)
// Aguarda mensagem de sucesso
const successMessage = await screen.findByText('Message sent successfully!')
expect(successMessage).toBeInTheDocument()
})
Teste de Estado de Erro:
import { rest } from 'msw'
test('displays error when API fails', async () => {
// Simula falha da API
server.use(
rest.get('/api/users', (req, res, ctx) => {
return res(ctx.status(500))
})
)
render(<UserList />)
// Aguarda mensagem de erro aparecer
const errorMessage = await screen.findByText(/failed to load users/i)
expect(errorMessage).toBeInTheDocument()
// Verifica que estado de carregamento se foi
const loadingSpinner = screen.queryByTestId('loading-spinner')
expect(loadingSpinner).not.toBeInTheDocument()
})
Conclusão
Dominar as consultas DOM do React Testing Library requer entender quando usar getBy para elementos imediatos, findBy para conteúdo assíncrono e queryBy para testar ausência de elementos. Priorize consultas focadas em acessibilidade como getByRole e getByLabelText, e reserve getByTestId para casos extremos onde consultas semânticas não são suficientes.
A chave para testes confiáveis é escolher consultas que espelhem como os usuários interagem com seus componentes. Esta abordagem cria testes que são tanto robustos quanto mantíveis, capturando problemas reais enfrentados pelos usuários enquanto permanecem resilientes a mudanças de implementação.
Perguntas Frequentes
Use consultas findBy ao testar elementos que aparecem após operações assíncronas como chamadas de API, setTimeout ou atualizações de estado. As consultas findBy automaticamente tentam novamente até que o elemento apareça ou o timeout seja atingido, prevenindo testes instáveis.
As consultas getByTestId não refletem como os usuários interagem com sua aplicação. Os usuários não veem test IDs - eles interagem com botões, leem texto e usam rótulos de formulário. Consultas semânticas como getByRole e getByText criam testes mais realistas e mantíveis.
Use consultas queryBy, que retornam null quando elementos não são encontrados em vez de lançar erros. Por exemplo: expect(screen.queryByText('Error message')).not.toBeInTheDocument().
As consultas getAllBy retornam arrays de elementos imediatamente e lançam erros se nenhum elemento for encontrado. As consultas findAllBy retornam promises que resolvem para arrays e aguardam pelo menos um elemento aparecer antes de resolver.
Use screen.debug() para ver a estrutura DOM atual, verifique as mensagens de erro para sugestões de consultas e experimente o Testing Playground para experimentar com diferentes abordagens de consulta no seu HTML real.