Back

Cómo Consultar el DOM en React Testing Library

Cómo Consultar el DOM en React Testing Library

Las consultas DOM de React Testing Library son la base de las pruebas efectivas de componentes, sin embargo, muchos desarrolladores tienen dificultades para elegir el método de consulta correcto para sus escenarios específicos de testing. Ya sea que estés trabajando con elementos síncronos, componentes asíncronos o renderizado condicional, entender cuándo usar los métodos getBy, findBy y queryBy puede marcar la diferencia entre pruebas confiables y pruebas inestables.

Esta guía cubre los métodos esenciales de consulta de React Testing Library, sus diferencias de comportamiento y ejemplos prácticos para ayudarte a escribir pruebas más robustas. Aprenderás las pautas de prioridad de consultas, errores comunes que debes evitar y patrones de testing del mundo real que mejoran la confiabilidad de las pruebas.

Puntos Clave

  • Usa consultas getBy para elementos que deben estar presentes inmediatamente después del renderizado
  • Usa consultas findBy para elementos que aparecen de forma asíncrona, como después de llamadas API o actualizaciones de estado
  • Usa consultas queryBy cuando pruebes que los elementos no están presentes en el DOM
  • Prioriza las consultas getByRole y getByLabelText para mejores pruebas de accesibilidad
  • Reserva getByTestId para escenarios complejos donde las consultas semánticas no son prácticas
  • Depura consultas fallidas usando screen.debug() y Testing Playground para una mejor selección de consultas

Comenzando con las Consultas DOM de React Testing Library

React Testing Library se construye sobre DOM Testing Library agregando APIs específicamente diseñadas para probar componentes React. Instálalo en tu proyecto React:

npm install --save-dev @testing-library/react @testing-library/jest-dom

Las consultas DOM de React Testing Library funcionan encontrando elementos en el árbol DOM del componente renderizado, similar a como los usuarios interactúan con tu aplicación. Aquí tienes un ejemplo 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()
})

La diferencia clave con DOM Testing Library es que React Testing Library maneja automáticamente las preocupaciones específicas de React como el renderizado de componentes, actualizaciones de estado y limpieza.

Entendiendo los Tipos de Consulta de React Testing Library

React Testing Library proporciona tres tipos principales de consulta, cada uno con diferentes comportamientos para manejar elementos faltantes y temporización.

Consultas getBy - Selección Síncrona de Elementos

Las consultas getBy devuelven elementos inmediatamente y lanzan errores si los elementos no se encuentran o si existen múltiples coincidencias:

// Elemento único - lanza error si no se encuentra o si encuentra múltiples
const button = screen.getByRole('button', { name: 'Submit' })

// Múltiples elementos - lanza error si no encuentra ninguno
const listItems = screen.getAllByRole('listitem')

Usa consultas getBy cuando esperes que los elementos estén presentes en el DOM inmediatamente después del renderizado.

Consultas findBy - Consultas DOM Asíncronas

Las consultas findBy devuelven promesas y reintentan hasta que los elementos aparecen o se agota el tiempo de espera (por defecto 1000ms):

// Esperar a que aparezca un elemento asíncrono
const successMessage = await screen.findByText('Profile updated successfully')

// Múltiples elementos asíncronos
const loadedItems = await screen.findAllByTestId('product-card')

Usa consultas findBy para elementos que aparecen después de llamadas API, actualizaciones de estado u otras operaciones asíncronas.

Consultas queryBy - Testing de Elementos Condicionales

Las consultas queryBy devuelven null cuando los elementos no se encuentran, haciéndolas perfectas para probar la ausencia de elementos:

// Probar que el elemento no existe
const errorMessage = screen.queryByText('Error occurred')
expect(errorMessage).not.toBeInTheDocument()

// Múltiples elementos - devuelve array vacío si no encuentra ninguno
const hiddenElements = screen.queryAllByTestId('hidden-item')
expect(hiddenElements).toHaveLength(0)
Tipo de Consulta0 Coincidencias1 Coincidencia>1 CoincidenciasAsíncrono
getByLanza errorDevuelve elementoLanza errorNo
queryByDevuelve nullDevuelve elementoLanza errorNo
findByLanza errorDevuelve elementoLanza error

Guía de Prioridad de Consultas de React Testing Library

React Testing Library fomenta probar componentes como los usuarios interactúan con ellos. Esta guía de prioridad te ayuda a elegir consultas que reflejen el comportamiento real del usuario.

Consultas Centradas en Accesibilidad (getByRole, getByLabelText)

getByRole debería ser tu primera opción para la mayoría de elementos:

// Botones, enlaces, controles de formulario
const submitButton = screen.getByRole('button', { name: 'Create Account' })
const navigationLink = screen.getByRole('link', { name: 'About Us' })

// Encabezados con niveles específicos
const pageTitle = screen.getByRole('heading', { level: 1 })

getByLabelText funciona mejor para campos de formulario:

const emailInput = screen.getByLabelText('Email Address')
const passwordInput = screen.getByLabelText(/password/i)

Estas consultas aseguran que tus componentes funcionen con tecnologías de asistencia.

Consultas Basadas en Contenido (getByText, getByPlaceholderText)

getByText encuentra elementos por su contenido de texto visible:

// Coincidencia exacta de texto
const welcomeMessage = screen.getByText('Welcome back, John!')

// Regex para coincidencia flexible
const errorText = screen.getByText(/something went wrong/i)

getByPlaceholderText ayuda cuando las etiquetas no están disponibles:

const searchInput = screen.getByPlaceholderText('Search products...')

Cuándo Usar getByTestId

Reserva getByTestId para casos donde las consultas semánticas no son suficientes:

// Contenido dinámico donde el texto cambia
const userAvatar = screen.getByTestId('user-avatar')

// Componentes complejos sin roles claros
const chartContainer = screen.getByTestId('sales-chart')

Agrega test IDs con moderación y prefiere consultas semánticas siempre que sea posible.

Errores Comunes con las Consultas DOM de React Testing Library

Evitando Pruebas Inestables con la Selección Correcta de Consultas

Problema: Usar consultas getBy para elementos que se cargan de forma asíncrona:

// ❌ Inestable - el elemento podría no estar cargado aún
test('shows user profile', () => {
  render(<UserProfile userId="123" />)
  const userName = screen.getByText('John Doe') // Puede fallar
})

Solución: Usa findBy para elementos asíncronos:

// ✅ Confiable - espera a que aparezca el elemento
test('shows user profile', async () => {
  render(<UserProfile userId="123" />)
  const userName = await screen.findByText('John Doe')
  expect(userName).toBeInTheDocument()
})

Depurando Consultas Fallidas

Cuando las consultas fallan, React Testing Library proporciona herramientas útiles de depuración:

// Ver qué hay realmente en el DOM
screen.debug()

// Obtener sugerencias para mejores consultas
screen.getByRole('button') // El mensaje de error sugiere roles disponibles

Usa Testing Playground para experimentar con consultas en tu HTML real.

Dependencia Excesiva de Test IDs

Problema: Usar getByTestId como método de consulta por defecto:

// ❌ No centrado en el usuario
const button = screen.getByTestId('submit-button')

Solución: Usa consultas semánticas que reflejen la interacción del usuario:

// ✅ Centrado en el usuario
const button = screen.getByRole('button', { name: 'Submit Form' })

Los test IDs deberían ser tu último recurso, no tu primera opción.

Ejemplos del Mundo Real con React Testing Library

Aquí tienes ejemplos prácticos mostrando diferentes métodos de consulta en acción:

Testing de Formularios:

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

test('handles form submission', async () => {
  render(<ContactForm />)
  
  // Usa getByLabelText para campos de formulario
  const nameInput = screen.getByLabelText('Full Name')
  const emailInput = screen.getByLabelText('Email')
  const submitButton = screen.getByRole('button', { name: 'Send Message' })
  
  // Llenar formulario y enviar
  fireEvent.change(nameInput, { target: { value: 'John Doe' } })
  fireEvent.change(emailInput, { target: { value: 'john@example.com' } })
  fireEvent.click(submitButton)
  
  // Esperar mensaje de éxito
  const successMessage = await screen.findByText('Message sent successfully!')
  expect(successMessage).toBeInTheDocument()
})

Testing de Estados de Error:

import { rest } from 'msw'

test('displays error when API fails', async () => {
  // Simular fallo de API
  server.use(
    rest.get('/api/users', (req, res, ctx) => {
      return res(ctx.status(500))
    })
  )
  
  render(<UserList />)
  
  // Esperar a que aparezca el mensaje de error
  const errorMessage = await screen.findByText(/failed to load users/i)
  expect(errorMessage).toBeInTheDocument()
  
  // Verificar que el estado de carga se ha ido
  const loadingSpinner = screen.queryByTestId('loading-spinner')
  expect(loadingSpinner).not.toBeInTheDocument()
})

Conclusión

Dominar las consultas DOM de React Testing Library requiere entender cuándo usar getBy para elementos inmediatos, findBy para contenido asíncrono y queryBy para probar la ausencia de elementos. Prioriza consultas centradas en accesibilidad como getByRole y getByLabelText, y reserva getByTestId para casos extremos donde las consultas semánticas no son suficientes.

La clave para pruebas confiables es elegir consultas que reflejen cómo los usuarios interactúan con tus componentes. Este enfoque crea pruebas que son tanto robustas como mantenibles, capturando problemas reales que enfrentan los usuarios mientras permanecen resistentes a cambios de implementación.

Preguntas Frecuentes

Usa consultas findBy cuando pruebes elementos que aparecen después de operaciones asíncronas como llamadas API, setTimeout o actualizaciones de estado. Las consultas findBy automáticamente reintentan hasta que el elemento aparece o se agota el tiempo de espera, previniendo pruebas inestables.

Las consultas getByTestId no reflejan cómo los usuarios interactúan con tu aplicación. Los usuarios no ven test IDs - interactúan con botones, leen texto y usan etiquetas de formulario. Las consultas semánticas como getByRole y getByText crean pruebas más realistas y mantenibles.

Usa consultas queryBy, que devuelven null cuando los elementos no se encuentran en lugar de lanzar errores. Por ejemplo: expect(screen.queryByText('Error message')).not.toBeInTheDocument().

Las consultas getAllBy devuelven arrays de elementos inmediatamente y lanzan errores si no se encuentran elementos. Las consultas findAllBy devuelven promesas que se resuelven a arrays y esperan a que aparezca al menos un elemento antes de resolverse.

Usa screen.debug() para ver la estructura DOM actual, revisa los mensajes de error para sugerencias de consultas, y prueba Testing Playground para experimentar con diferentes enfoques de consulta en tu HTML real.

Listen to your bugs 🧘, with OpenReplay

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