@testing-library/react-native (RNTL) prueba componentes de la forma en que los usuarios interactúan con ellos - por texto visible, roles de accesibilidad y etiquetas - no por detalles de implementación como estado interno o métodos privados.
RNTL renderiza componentes con Test Renderer - un árbol del lado de JS que refleja componentes host de React Native (View, Text, Pressable).
Las consultas caminan por el árbol renderizado buscando nodos que coincidan con accesibilidad y texto - las mismas señales en las que se basan la tecnología de asistencia y los usuarios.
screen es un namespace vinculado al render() más reciente - vuelve a consultar después de actualizaciones de estado en lugar de cachear referencias de elementos de antes de re-renderizados.
userEvent programa interacciones como un usuario real (presionar, escribir, desplazar) y devuelve promesas - fireEvent sincrónico aún existe pero userEvent es preferido para pruebas nuevas.
Las utilidades asincronas (waitFor, findBy*) sondean hasta que las aserciones pasen o agoten el tiempo de espera - reemplazan la gimnasia manual de setTimeout + act.
getBy* lanza si hay cero o muchas coincidencias. queryBy* devuelve null cuando está ausente (bueno para aserciones negativas). findBy* envuelve waitFor + getBy* para elementos que aparecen después del trabajo asincrónico.
// El elemento aparece después de fetch - prefiere findBy*expect(await screen.findByText("Bienvenido de nuevo")).toBeOnTheScreen();// Callback o mock invocado después de descarga de estadoawait waitFor(() => { expect(onSubmit).toHaveBeenCalled();});// Múltiples aserciones después de una interacciónawait user.press(screen.getByRole("button", { name: "Actualizar" }));await waitFor(() => { expect(screen.getByText("Actualizado")).toBeOnTheScreen();});
El tiempo de espera predeterminado de waitFor es 1000 ms - pasa { timeout: 3000 } para redes mocked lentas, no para timing de producción.
Referencias de elementos en caché después de rerender - Nodos obsoletos de antes de una actualización de estado causan fallas de "elemento no encontrado". Solución: Vuelve a consultar con screen.getBy* dentro de waitFor o limita las consultas al render más reciente.
await faltante en userEvent - Presión y escritura son asincronos; olvidar await compite con aserciones. Solución:await user.press(...) y await user.type(...) en cada prueba.
getByTestId como predeterminado - testID es invisible para los usuarios y diverge de cambios de copia. Solución: Agrega accessibilityLabel / accessibilityRole en componentes; reserva testID para elementos de lista o casos extremos de driver nativo.
fireEvent para flujos complejos - fireEvent.press omite la canalización de eventos que userEvent ejercita. Solución: Prefiere userEvent.setup() para interacciones; mantén fireEvent para casos raros de bajo nivel.
Pruebas de detalles de implementación - Asertir component.state.count o renderizados superficiales al estilo enzyme rompe en refactores. Solución: Aserta resultados visibles: texto, roles, callbacks, estado de accesibilidad.
Elementos de FlatList no encontrados - Las listas virtualizadas pueden no montar filas fuera de pantalla. Solución: Pasa pequeñas matrices data en pruebas, establece initialNumToRender alto en un envoltorio de prueba o prueba componentes de fila de forma aislada.
No importar RNTL en la configuración de Jest - Matchers como toBeOnTheScreen lanzan "matcher no encontrado". Solución:import "@testing-library/react-native" en jest.setup.ts (ver Configuración de Jest para Expo).
Misma filosofía - consulta por lo que ven los usuarios. Las APIs se alinean (render, screen, userEvent), pero RN usa accessibilityRole, accessibilityLabel y Pressable en lugar de roles DOM y click.
¿Necesito @testing-library/jest-native?
No para nuevos proyectos. RNTL registra matchers (toBeOnTheScreen, toHaveAccessibilityState) cuando importas desde @testing-library/react-native. @testing-library/jest-native es heredado.
¿getByText vs getByRole para botones?
Prefiere getByRole("button", { name: "Iniciar sesión" }) - verifica que el control se exponga como botón a la tecnología de asistencia. getByText("Iniciar sesión") solo no confirma la presionabilidad.
¿Cuándo debo usar findBy*?
Cuando el elemento aparece después del trabajo asincrónico - datos obtenidos, useEffect, transición de navegación. findBy* combina waitFor + getBy* y devuelve una promesa que await.
Usa queryBy* - getBy* lanza cuando el elemento falta.
¿Puedo probar hooks personalizados sin renderizar una pantalla?
Sí - renderHook desde @testing-library/react-native envuelve el hook en un arnés de prueba. Usa act() cuando el hook actualiza estado sincronamente.
¿Cómo pruebo cambios de TextInput?
const user = userEvent.setup();await user.type(screen.getByLabelText("Email"), "alex@example.com");
O fireEvent.changeText para control directo cuando no simulas pulsaciones de teclas. Prefiere userEvent para pruebas centradas en el usuario.
¿Por qué mi prueba de FlatList no encuentra una fila?
La virtualización puede no montar elementos fuera de pantalla - mantén data pequeño en pruebas.
Extrae el componente de fila y pruébalo con props directamente.
Asegúrate de que keyExtractor devuelva claves estables para que las celdas se reconcilien predeciblemente.
¿Cómo pruebo modales e interfaz de usuario condicional?
Abre el modal a través de userEvent.press en el disparador, luego await waitFor o findBy* para contenido modal. Consulta dentro del modal por rol/texto - no por nombre de portal (RN no tiene portal DOM).
¿Debo usar screen o desestructuración desde render?
screen es preferido - las consultas siempre apuntan al render más reciente sin pasar getByText alrededor. La desestructuración desde render() es aceptable para pruebas de consulta única.
Usa mapas de rutas en línea o directorios de accesorios; consulta los documentos de renderRouter de Expo para patrones de anulación.
¿Qué hay sobre MSW o mock de fetch?
Mock en el límite que tu componente usa - jest.mock en el módulo de API o MSW en Node para pruebas de integración. Los presentadores deben recibir datos a través de props para que los mocks de fetch permanezcan en pruebas de contenedor.
fireEvent vs userEvent - ¿cuál gana?
userEvent para nuevas pruebas - modela entrada secuencial del usuario. fireEvent es de nivel inferior y puede ocultar bugs de await faltante cuando se mezcla con actualizaciones de estado asincrónico.
¿Cómo depuro el árbol renderizado?
import { render, screen, debug } from "@testing-library/react-native";render(<MyComponent />);screen.debug(); // imprime árbol host a consola
Usa con moderación - prefiere fallos explícitos de queryBy* que te digan qué está en pantalla.