Acerca de las consultas
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Resumen
Las consultas son los métodos que Testing Library proporciona para encontrar elementos en la página. Existen varios tipos de consultas ("get", "find", "query"); la diferencia entre ellos radica en si la consulta lanzará un error cuando no se encuentre ningún elemento o si retornará una Promise y reintentará. Dependiendo del contenido de la página que estés seleccionando, diferentes consultas pueden ser más o menos apropiadas. Consulta la guía de prioridad para recomendaciones sobre cómo utilizar consultas semánticas y probar tu página de la manera más accesible.
Después de seleccionar un elemento, puedes usar la API de Eventos o user-event para disparar eventos y simular interacciones de usuario con la página, o utilizar Jest y jest-dom para realizar aserciones sobre el elemento.
Existen métodos auxiliares de Testing Library que funcionan con consultas. A medida que los elementos aparecen y desaparecen en respuesta a acciones, se pueden usar APIs asíncronas como waitFor o consultas findBy para esperar los cambios en el DOM. Para encontrar solo elementos que sean hijos de un elemento específico, puedes usar within. Si es necesario, también hay opciones que puedes configurar, como el tiempo de espera para reintentos y el atributo testID predeterminado.
Ejemplo
import {render, screen} from '@testing-library/react' // (or /dom, /vue, ...)
test('should show login form', () => {
render(<Login />)
const input = screen.getByLabelText('Username')
// Events and assertions...
})
Tipos de consultas
Elementos únicos
getBy...: Retorna el nodo coincidente para una consulta, y lanza un error descriptivo si no se encuentran elementos o si se encuentra más de una coincidencia (usagetAllBysi se espera más de un elemento).queryBy...: Retorna el nodo coincidente para una consulta, y retornanullsi no se encuentran elementos. Útil para verificar que un elemento no está presente. Lanza un error si se encuentra más de una coincidencia (usaqueryAllBysi esto es aceptable).findBy...: Retorna una Promise que se resuelve cuando se encuentra un elemento que coincide con la consulta. La promise se rechaza si no se encuentra ningún elemento o si se encuentra más de un elemento después de un tiempo de espera predeterminado de 1000ms. Si necesitas encontrar más de un elemento, usafindAllBy.
Múltiples elementos
getAllBy...: Retorna un array de todos los nodos coincidentes para una consulta, y lanza un error si no se encuentran elementos.queryAllBy...: Retorna un array de todos los nodos coincidentes para una consulta, y retorna un array vacío ([]) si no se encuentran elementos.findAllBy...: Retorna una promise que se resuelve en un array de elementos cuando se encuentran elementos que coinciden con la consulta. La promise se rechaza si no se encuentran elementos después de un tiempo de espera predeterminado de1000ms.- Los métodos
findBycombinan consultasgetBy*ywaitFor. Aceptan las opciones dewaitForcomo último argumento (ej.await screen.findByText('text', queryOptions, waitForOptions))
- Los métodos
Summary Table
| Type of Query | 0 Matches | 1 Match | >1 Matches | Retry (Async/Await) |
|---|---|---|---|---|
| Single Element | ||||
getBy... | Throw error | Return element | Throw error | No |
queryBy... | Return null | Return element | Throw error | No |
findBy... | Throw error | Return element | Throw error | Yes |
| Multiple Elements | ||||
getAllBy... | Throw error | Return array | Return array | No |
queryAllBy... | Return [] | Return array | Return array | No |
findAllBy... | Throw error | Return array | Return array | Yes |
Prioridad
Basado en los Principios rectores, tus pruebas deben imitar cómo los usuarios interactúan con tu código (componente, página, etc.) tanto como sea posible. Con esto en mente, recomendamos este orden de prioridad:
Consultas accesibles para todos: Consultas que reflejan la experiencia de usuarios visuales/de ratón y quienes utilizan tecnologías asistivas.
getByRole: Puede usarse para consultar cada elemento expuesto en el árbol de accesibilidad. Con la opciónnamepuedes filtrar elementos por su nombre accesible. Debería ser tu primera opción para casi todo. Es raro que no puedas obtener algo con esto (si no puedes, posiblemente tu UI sea inaccesible). Normalmente se usa con la opciónnameasí:getByRole('button', {name: /submit/i}). Consulta la lista de roles.getByLabelText: Ideal para campos de formulario. Al navegar formularios, los usuarios encuentran elementos mediante etiquetas. Este método emula ese comportamiento, siendo preferible.getByPlaceholderText: Un placeholder no sustituye una etiqueta. Pero si es lo único disponible, es mejor que alternativas.getByText: Fuera de formularios, el contenido textual es cómo los usuarios encuentran elementos. Útil para elementos no interactivos (divs, spans, párrafos).getByDisplayValue: El valor actual de elementos de formulario ayuda al navegar páginas con valores prellenados.
Consultas semánticas: Selectores compatibles con HTML5 y ARIA. Nota: la experiencia de usuario con estos atributos varía entre navegadores y tecnologías asistivas.
getByAltText: Si tu elemento soporta textoalt(img,area,input, elementos personalizados), úsalo para encontrarlo.getByTitle: El atributo title no es leído consistentemente por lectores de pantalla ni visible por defecto para usuarios videntes.
IDs de prueba
getByTestId: Los usuarios no ven/oyen estos, solo recomendado cuando no puedes hacer match por rol/texto o no tiene sentido (ej. texto dinámico).
Uso de consultas
Las consultas base de DOM Testing Library requieren pasar un container como primer argumento. La mayoría de implementaciones para frameworks proporcionan versiones previnculadas al renderizar componentes, evitando especificar contenedores. Además, para consultar document.body puedes usar el export screen como se muestra abajo (se recomienda screen).
El argumento principal de una consulta puede ser string, expresión regular o función. Hay opciones para ajustar cómo se analiza el texto. Ver TextMatch para documentación sobre qué puede pasarse.
Dados los siguientes elementos DOM (renderizados por React, Vue, Angular o HTML simple):
<body>
<div id="app">
<label for="username-input">Username</label>
<input id="username-input" />
</div>
</body>
Puedes usar una consulta para encontrar un elemento (en este caso, byLabelText):
import {screen, getByLabelText} from '@testing-library/dom'
// With screen:
const inputNode1 = screen.getByLabelText('Username')
// Without screen, you need to provide a container:
const container = document.querySelector('#app')
const inputNode2 = getByLabelText(container, 'Username')
queryOptions
Puedes pasar un objeto queryOptions con el tipo de consulta. Consulta la documentación de cada tipo para ver opciones disponibles, ej. API byRole.
screen
Todas las consultas exportadas por DOM Testing Library aceptan un container como primer argumento. Dado que consultar todo el document.body es muy común, DOM Testing Library también exporta un objeto screen que contiene todas las consultas previnculadas a document.body (usando la funcionalidad within). Wrappers como React Testing Library reexportan screen para que puedas usarlo de la misma manera.
Así es como se usa:
- Native
- React
- Angular
- Cypress
import {screen} from '@testing-library/dom'
document.body.innerHTML = `
<label for="example">Example</label>
<input id="example" />
`
const exampleInput = screen.getByLabelText('Example')
import {render, screen} from '@testing-library/react'
render(
<div>
<label htmlFor="example">Example</label>
<input id="example" />
</div>,
)
const exampleInput = screen.getByLabelText('Example')
import {render, screen} from '@testing-library/angular'
await render(`
<div>
<label for="example">Example</label>
<input id="example" />
</div>
`)
const exampleInput = screen.getByLabelText('Example')
cy.findByLabelText('Example').should('exist')
Nota
Necesitas un entorno DOM global para usar
screen. Si usas Jest con testEnvironment configurado comojsdom, tendrás disponible un entorno DOM global.Si cargas tu prueba con una etiqueta
script, asegúrate de que venga después delbody. Puedes ver un ejemplo aquí.
TextMatch
La mayoría de las APIs de consulta aceptan un TextMatch como argumento, lo que significa que puede ser una cadena, una expresión regular o una función con firma (content?: string, element?: Element | null) => boolean que retorna true para coincidencia y false para no coincidencia.
Ejemplos de TextMatch
Dado el siguiente HTML:
<div>Hello World</div>
Encontrará el div:
// Matching a string:
screen.getByText('Hello World') // full string match
screen.getByText('llo Worl', {exact: false}) // substring match
screen.getByText('hello world', {exact: false}) // ignore case
// Matching a regex:
screen.getByText(/World/) // substring match
screen.getByText(/world/i) // substring match, ignore case
screen.getByText(/^hello world$/i) // full string match, ignore case
screen.getByText(/Hello W?oRlD/i) // substring match, ignore case, searches for "hello world" or "hello orld"
// Matching with a custom function:
screen.getByText((content, element) => content.startsWith('Hello'))
No encontrará el div:
// full string does not match
screen.getByText('Goodbye World')
// case-sensitive regex with different case
screen.getByText(/hello world/)
// function looking for a span when it's actually a div:
screen.getByText((content, element) => {
return element.tagName.toLowerCase() === 'span' && content.startsWith('Hello')
})
Precisión
Las consultas que aceptan TextMatch también admiten un objeto como último argumento con opciones que afectan la precisión de coincidencia de cadenas:
exact: Por defectotrue; coincide con cadenas completas, sensible a mayúsculas. Cuando es false, coincide con subcadenas sin distinguir mayúsculas/minúsculas.- No tiene efecto cuando se usa con argumentos
regexofunction. - En la mayoría de casos, usar una expresión regular en vez de una cadena con
{ exact: false }da más control sobre coincidencias aproximadas, por lo que es preferible.
- No tiene efecto cuando se usa con argumentos
normalizer: Función opcional que sobreescribe el comportamiento de normalización. VerNormalization.
Normalización
Antes de aplicar cualquier lógica de coincidencia sobre texto en el DOM, DOM Testing Library normaliza automáticamente ese texto. Por defecto, la normalización incluye recortar espacios en blanco al inicio/final del texto y colapsar múltiples espacios adyacentes dentro de la cadena en un solo espacio.
Si deseas prevenir esta normalización o proporcionar una normalización alternativa (ej. para remover caracteres Unicode de control), puedes proveer una función normalizer en el objeto de opciones. Esta función recibirá una cadena y debe retornar una versión normalizada de la misma.
Nota
Especificar un valor para
normalizerreemplaza la normalización incorporada, pero puedes llamar agetDefaultNormalizerpara obtener un normalizador integrado, ya sea para ajustar esa normalización o llamarlo desde tu propio normalizador.
getDefaultNormalizer acepta un objeto de opciones que permite seleccionar comportamientos:
trim: Por defectotrue. Recorta espacios en blanco iniciales y finales.collapseWhitespace: Por defectotrue. Colapsa espacios internos (saltos de línea, tabulaciones, espacios repetidos) en un solo espacio.
Ejemplos de Normalización
Para realizar una coincidencia sin recortar texto:
screen.getByText('text', {
normalizer: getDefaultNormalizer({trim: false}),
})
Para sobreescribir la normalización removiendo algunos caracteres Unicode mientras se mantiene parte (no toda) del comportamiento de normalización incorporado:
screen.getByText('text', {
normalizer: str =>
getDefaultNormalizer({trim: false})(str).replace(/[\u200E-\u200F]*/g, ''),
})
Consultas Manuales
Además de las consultas proporcionadas por la biblioteca de pruebas, puedes utilizar la API DOM regular querySelector para buscar elementos. Ten en cuenta que usar esto como solución alternativa para consultar por clase o ID no es recomendable porque son invisibles para el usuario. Si es necesario, utiliza un testid para dejar clara tu intención de recurrir a consultas no semánticas y establecer un contrato de API estable en el HTML.
// @testing-library/react
const {container} = render(<MyComponent />)
const foo = container.querySelector('[data-foo="bar"]')
Extensión de navegador
¿Sigues teniendo problemas para entender cómo usar las consultas de Testing Library?
Existe una extensión de navegador muy útil para Chrome llamada Testing Playground que te ayuda a encontrar las mejores consultas para seleccionar elementos. Te permite inspeccionar las jerarquías de elementos en las Herramientas de Desarrollo del navegador y ofrece sugerencias sobre cómo seleccionarlos, fomentando buenas prácticas de pruebas.
Entorno de pruebas
Si deseas familiarizarte más con estas consultas, puedes probarlas en testing-playground.com. Testing Playground es un entorno interactivo donde puedes ejecutar diferentes consultas contra tu propio HTML y obtener retroalimentación visual que sigue las reglas mencionadas anteriormente.