Saltar al contenido principal
Móvil Tutorial Capacitor

React Native Testing Library: Cómo testear apps correctamente

Domina React Native Testing Library con tips de setup, queries, mocking y CI. Crea tests fiables centrados en el usuario para componentes, hooks y navegación.

React Native Testing Library: Cómo testear apps correctamente

Tu suite de tests de React Native está en verde, pero un usuario reporta que al pulsar “Continuar” no pasa nada en un dispositivo real. El test del componente encontró el botón, ejecutó su handler y vio la pantalla esperada en un entorno de JavaScript mockeado. Pero nunca verificó el prompt de permisos nativos, el comportamiento del teclado, las animaciones, la API de la plataforma o el stack de navegación real.

Ese vacío es donde los equipos ganan una falsa confianza. React Native Testing Library es excelente para testear qué renderiza un componente y cómo responde a la interacción del usuario, pero no sustituye al testing en dispositivo o a la medición de performance. La estrategia fiable es usar cada capa para los fallos que puede exponer.

Table of Contents

Por qué el testing centrado en el usuario lo cambia todo

Un fallo común en los tests ocurre antes de escribirlos. Un desarrollador inspecciona las props de un componente, accede a su estado o compara un snapshot gigante porque esos detalles son fáciles de asertar. El test pasa, pero luego un refactor cambia la estructura del componente sin alterar la experiencia y la suite se rompe. Peor aún, el test puede seguir pasando mientras el comportamiento visible para el usuario es incorrecto, porque la aserción nunca describió lo que el usuario necesita ver.

React Native Testing Library adopta el enfoque opuesto. Renderizas el componente, interactúas con él mediante un control visible y luego asertas el resultado que un usuario puede observar. Ese enfoque coincide con el ejemplo centrado en el usuario de React Native Testing Library, así como con la guía de testing de React Native para mantener los tests cortos, enfocar cada test en una sola cosa, separar la vista de la lógica de negocio y el estado, y preferir el output visible o helpers de accesibilidad sobre los detalles de implementación interna.

Un diagrama que ilustra los beneficios del testing centrado en el usuario, destacando el comportamiento del usuario, la resiliencia, la fiabilidad y las mejores prácticas.

Test the behavior users depend on

Supongamos que un formulario de login deshabilita su botón de envío mientras se ejecuta una petición. Un test frágil podría inspeccionar disabled en una instancia de componente específica o verificar que una variable de estado cambió. Un test más robusto presiona el control accesible “Sign in”, espera al indicador de carga y comprueba que aparece un mensaje de error o la pantalla de destino.

Al segundo test no le importa si el formulario usa estado local, un reducer, un custom hook o una implementación de botón distinta. Le importa que la app comunique el resultado correcto.

Regla práctica: Si un usuario no puede observarlo, cuestiónate si debería formar parte de un test de comportamiento de componente.

Las queries basadas en roles y labels de accesibilidad también fuerzan un mejor code del producto. Una pantalla que expone labels significativos es más fácil de usar con tecnología asistiva y más fácil de testear. Esa conexión es clave al evaluar la experiencia de usuario de la app, porque la testabilidad y la usabilidad suelen mejorar a la par.

Lo que los tests aprobados no demuestran

Un test de componente centrado en el usuario puede demostrar que JavaScript renderiza la rama esperada y responde a un press simulado. No puede demostrar que un prompt biométrico se abra correctamente, que una cámara nativa devuelva un resultado utilizable o que un flujo de pago sobreviva al ciclo de vida específico de la plataforma.

Ese límite no es una debilidad de la librería. Es una razón para mantener la integridad de las capas de test. Usa RNTL para el comportamiento de componentes, y reserva los tests basados en dispositivo para flujos donde la integración nativa, la navegación real, los permisos, la autenticación, los pagos o la funcionalidad core de la app puedan cambiar el resultado.

Instalación y configuración que realmente funciona

La configuración moderna comienza con el paquete scoped:

@testing-library/react-native

El antiguo react-native-testing-library nombre del paquete npm aún existe como un registro histórico, pero los proyectos actuales deben usar el paquete scoped mantenido por la familia Testing Library. El proyecto repositorio de GitHub lo describe como utilidades de React Native para fomentar buenas prácticas de testing, y su historial de versiones muestra que la librería ha evolucionado junto con React Native en lugar de ser un helper estático.

Instala la librería con tu entorno Jest actual. Los proyectos de Expo suelen usar el preset de Expo Jest, mientras que los proyectos bare de React Native pueden usar el preset de React Native:

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

Para Expo, añade el preset que ya usa tu proyecto:

{
  "scripts": {
    "test": "jest"
  },
  "jest": {
    "preset": "jest-expo"
  }
}

Para una aplicación bare de React Native, usa la configuración de React Native Jest correspondiente. Mantén el preset alineado con la versión del framework. Muchos errores que parecen problemas de RNTL se deben a un desajuste en el renderer de React, el transform de Babel o el preset de Jest.

Mantén los archivos de setup explícitos

Pon la configuración compartida del entorno en un archivo de setup en lugar de repetir mocks en cada test:

// jest.setup.js
import '@testing-library/react-native/extend-expect';

Luego referéncialo desde Jest:

{
  "jest": {
    "preset": "jest-expo",
    "setupFilesAfterEnv": ["<rootDir>/jest.setup.js"]
  }
}

Si tu app usa React Navigation, Reanimated, gestión de gestos, safe-area context o módulos de storage, configura solo los mocks que tu entorno de test necesite. Un mock global que altere el comportamiento de la app puede facilitar que todos los tests pasen, pero restará fiabilidad a la suite.

TypeScript requiere la misma atención. Asegúrate de que Jest transforme .ts y .tsx los archivos mediante el preset o tu configuración de Babel, y mantén los tipos de test disponibles para el compilador. Un test que corre localmente pero no tiene type-checking puede ocultar nombres de query incorrectos, parámetros de navegación inválidos o shapes de mocks inseguros.

La cronología de versiones es útil para diagnosticar consejos de configuración antiguos. El proyecto lista 127 lanzamientos, con v14.0.1 etiquetada el 2026-06-23, mientras que v12.9.0, lanzada el 2024-11-27, añadió soporte oficial para React Native 0.77 y Expo 52. La línea alpha de la v14 en marzo de 2025 migró a Universal Test Renderer desde el ya deprecado React Test Renderer y se preparó para el soporte exclusivo de React 19. Estos detalles aparecen en el historial de releases de RNTL, así que no copies una dependencia de renderer de un tutorial desactualizado sin revisar las versiones de tu app.

Para una base sólida de Jest, compara tu configuración con esta unit tests en JavaScript, y luego ejecuta una pequeña prueba de componente antes de añadir mocks de navegación y nativos.

Una infografía que muestra los cinco pasos para instalar y configurar el proyecto React Native Testing Library.

Explicación de las Core APIs: Queries y Assertions

Los tests de RNTL son fiables cuando sus queries coinciden con lo que un usuario puede ver, encontrar y operar. El API es pequeño, pero elegir un selector que exponga detalles de implementación puede dar un falso positivo.

Empieza con render:

const screen = render(<LoginForm />);

Elige la consulta más relevante para el usuario. Prioriza las consultas de accesibilidad si el componente las expone, usa el texto visible cuando el comportamiento dependa del texto y reserva testID para casos sin un selector significativo para el usuario o donde se necesite un hook de integración estable.

Una pirámide infográfica que muestra el orden de prioridad recomendado para las consultas en la automatización de pruebas de software.

Elige la query según el tiempo y la intención

Cada familia de queries tiene un propósito distinto:

  • Presencia síncrona: Usa getByRole, getByText, o cualquier otra getBy query cuando el elemento ya debería existir. El test falla de inmediato si no es así.
  • Aparición asíncrona: Usa findByRole o findByText cuando el renderizado o la interacción provoca una actualización asíncrona.
  • Comprobación de ausencia: Usa queryByText o queryByTestId cuando un elemento puede faltar y se espera un resultado nulo en lugar de una excepción.
  • Selectores de fallback: Usa getByTestId deliberadamente. Proporciona un hook duradero para controles complejos, pero no debe sustituir las etiquetas de accesibilidad en toda la app.

Para tests de eventos enfocados, fireEvent.press es directo:

fireEvent.press(screen.getByRole('button', { name: 'Save' }));

Usa userEvent cuando la versión instalada permite una secuencia de interacción más realista. En cualquier caso, valida la UI resultante:

expect(await screen.findByText('Saved')).toBeTruthy();

Una aserción de handler es ideal para componentes cuyo contrato público es un callback de evento. Es insuficiente si es la única prueba de que el user journey funciona.

Prefiere aserciones específicas sobre snapshots

Buenas aserciones describen la pantalla:

expect(screen.getByText('Account created')).toBeTruthy();
expect(screen.getByRole('button', { name: 'Continue' })).toBeEnabled();

También pueden verificar el estado de accesibilidad, la selección y el feedback de validación visible. Evita revisar la lógica interna:

expect(screen.getByTestId('submit-button').props.onPress).toBeDefined();

Esa aserción prueba que existe una prop, no que la funcionalidad trabaje. Los snapshots pequeños e intencionales detectan cambios estructurales, mientras que los snapshots grandes de navegación o pantallas suelen generar reviews ruidosas y facilitan la aprobación de updates sin explicación.

El paquete Testing Library pertenece a la organización testing-library npm. Sus paquetes activos incluyen @testing-library/react-native, con la versión 13.3.3 publicada en 2026, según la información del información del repositorioLas convenciones de consulta compartidas ayudan en todas las plataformas, pero no determinan qué aserción representa el comportamiento de tu producto.

Para una comparación más amplia de prácticas de testing de componentes con Jest, consulta esta guía de unit testing en React. RNTL sigue limitándose al límite del componente renderizado en JavaScript. Los permisos nativos, stacks de navegación reales, teclados de dispositivos, timing de frames y el comportamiento de memoria requieren herramientas E2E o de performance en lugar de más mocks de componentes.

El siguiente video muestra el workflow de query y aserción en contexto.

Patrones prácticos para componentes, hooks y navegación

Una suite de tests útil sigue la estructura de la aplicación. Los componentes de presentación requieren tests de comportamiento directo, los hooks necesitan inputs y outputs controlados, la navegación requiere un provider lo suficientemente realista y los módulos nativos necesitan mocks que sean claramente distintos a la verificación en un dispositivo real.

Un portátil moderno sobre un escritorio de madera mostrando React Native code y un mockup de una app móvil.

Componentes de presentación

Mantén el test de un componente cerca de su contrato público:

const onSelect = jest.fn();

render(
  <PlanCard
    title="Team"
    description="Shared workspace"
    onSelect={onSelect}
  />
);

fireEvent.press(screen.getByRole('button', { name: 'Choose Team' }));

expect(onSelect).toHaveBeenCalled();

La etiqueta exacta debe coincidir con tu UI. Lo importante es que el test encuentre el control tal como lo haría un usuario o un servicio de accesibilidad, y confirme el resultado visible o el callback relevante. No hagas mock de cada componente hijo por defecto. Haz mock de límites costosos o no relacionados solo cuando oscurezcan el comportamiento que estás testeando.

Para un custom hook, usa renderHook cuando la versión instalada de RNTL lo permita:

const { result } = renderHook(() => useSearch());

await act(async () => {
  await result.current.submit('query');
});

expect(result.current.status).toBe('success');

El test del hook debe controlar el límite de la red o del repositorio, no reproducir toda la app. Testea la pantalla por separado para saber si el estado del hook se convierte en una UI útil.

Para el comportamiento de navegación, renderizar una pantalla dentro de un NavigationContainer y un pequeño test navigator suele ser más valioso que hacer mock de cada método de navegación. Presiona un control visible, espera al contenido de destino y valida el output de la nueva pantalla. Un useNavigation el mock sigue siendo adecuado para un botón pequeño cuya única función es despachar una ruta tipada, pero no validará el registro de rutas, parámetros o el comportamiento del navigator anidado.

Los datos asíncronos merecen la misma disciplina. Haz mock de la respuesta de API o del repositorio, renderiza la pantalla, valida el estado de carga, resuelve la petición y luego valida el resultado de éxito o error. Usa findBy queries para los elementos finales esperados y haz explícitas las peticiones rechazadas; de lo contrario, un test podría pasar porque el componente nunca llegó a la rama prevista.

Mocks de módulos nativos

Los mocks para AsyncStorage, permisos, cámaras, biometría y APIs de plataforma son útiles para tests de JavaScript deterministas. No son prueba de que la funcionalidad nativa funcione. Mantén el comportamiento del mock fiel al contrato del módulo, resetea las llamadas entre tests e incluye respuestas de error en lugar de modelar solo el happy path.

Escenario Mejor con RNTL Requiere validación E2E
Validación de formularios y errores visibles Sí No suele ser así
UI de carga, éxito y error desde un repositorio mockeado Sí Para flujos críticos de producción
Navegación entre pantallas registradas Sí, con un test navigator Sí, si importan gestos, deep links o el comportamiento de la plataforma
Decisiones de estado en AsyncStorage Sí, con un mock controlado Sí, si el inicio y la persistencia interactúan con el ciclo de vida nativo
Cámara, biometría, permisos o APIs de la plataforma Lógica de branching y fallback en JS Sí, en dispositivos reales o representativos
Layout, rendimiento de renderizado y code nativo No Sí, con un dispositivo o herramientas especializadas

El límite es práctico: haz un mock de la dependencia para probar tu lógica en JavaScript y luego ejecuta un test en dispositivo para confirmar el comportamiento real.

Depuración de tests inestables en CI y checks de rendimiento

Un test flaky suele indicar tiempo no controlado, estado compartido o una aserción que compite con la UI. Identifica la condición antes de aumentar los timeouts. Un timeout más largo puede ocultar el problema de scheduling y ralentizar la suite.

Usa findBy para elementos que deben aparecer tras una actualización. Usa waitFor para condiciones de estado o llamadas a mocks. Si usas fake timers, avanza el tiempo en el punto exacto de la interacción y restaura los timers reales después. Un act "warning" significa que React detectó una actualización fuera de su límite de interacción esperado. Corrige el await, la interacción del usuario o el flush del timer en lugar de silenciar el warning.

Haz que los fallos de CI sean reproducibles

Un job de CI fiable instala desde el lockfile, ejecuta el mismo comando de Jest que usas localmente y aísla el estado de los mocks. Limpia las llamadas a mocks entre tests, resetea módulos si el estado a nivel de módulo afecta el comportamiento y elimina la dependencia del orden de ejecución. El caching de Jest acelera el feedback, pero los cambios en dependencias o configuración requieren invalidar el cache correctamente.

Los fallos exclusivos de CI requieren comparar entornos antes de reescribir un componente. Revisa Node, el gestor de paquetes, la configuración de Jest workers, los timers y las variables de entorno. Intenta reproducir el mismo comando localmente y reduce el test fallido a la interacción mínima que exponga la diferencia.

La observabilidad cubre un vacío distinto. Monitoreo de la salud de la app proporciona contexto de errores en producción que las pruebas de componentes mockeados no pueden reproducir, incluyendo fallos vinculados a dispositivos reales e integraciones nativas.

Los flujos sensibles a la seguridad necesitan un segundo límite. Usa tests de componentes para validación y transiciones de estado, y añade cobertura a nivel de dispositivo para el handoff y el comportamiento nativo. Para flujos de un solo-code, consulta la guía sobre cómo testear flujos de verificación por SMS cuando esa integración es parte del flujo.

Trata el rendimiento como medición, no como aserción

Un test funcional puede confirmar que una lista se renderiza. No puede determinar con fiabilidad si un refactor cambió la duración o el número de renders, porque el runtime del test añade ruido. Reassure mide esos valores en un escenario, repite el escenario para reducir la varianza y aplica análisis estadístico antes de reportar un cambio significativo. Su documentación de performance testing también incluye reportes aptos para CI y revisión de pull requests.

Evita aserciones fijas como “este render debe terminar por debajo de un umbral determinado”. Un worker de CI saturado puede causar un falso negativo, mientras que un umbral muy permisivo puede ignorar una regresión real. Usa mediciones repetidas para detectar señales de regresión y luego inspecciona el componente y el perfil del dispositivo cuando la comparativa muestre una diferencia significativa.

Regla de medición: Los tests funcionales determinan si el comportamiento es correcto. Las herramientas de rendimiento indican si un escenario medido ha cambiado. Mantén esas preguntas separadas.

Putting It All Together and Moving Forward

React Native Testing Library debe situarse en la capa rápida y amplia de tu estrategia de testing. Debe cubrir el comportamiento de los componentes, cambios de estado visibles, accesibilidad, validaciones, estados con datos mockeados e integración entre componentes de JavaScript. Mantén esos tests enfocados en lo que el usuario puede observar y haz que los fallos apunten a un comportamiento específico en lugar de a un árbol de renderizado gigante.

La capa más pequeña basada en dispositivos debe proteger los flujos donde los mocks pueden fallar. React Native resumen de testing indica que RNTL no proporciona un runtime completo de React Native y no puede testear features nativas. La misma guía recomienda combinar los tests de componentes con herramientas E2E como Detox para flujos críticos, incluyendo autenticación, pagos y la funcionalidad principal de la app.

Ruta de migración práctica

No necesitas reescribir toda tu suite de tests de una sola vez.

  1. Conserva los tests de negocio más valiosos. Mueve la lógica de dominio y el estado puro a unit tests específicos donde ofrezcan un feedback claro.
  2. Primero, reemplaza las aserciones de implementación. Cambia los checks de props y estado interno por salidas visibles, estados de accesibilidad y resultados de interacción.
  3. Reduce los snapshots. Mantén solo los snapshots que los revisores puedan entender y mantener.
  4. Añade cobertura en los límites nativos. Para cada módulo mockeado importante, identifica el comportamiento del dispositivo que aún requiere validación.
  5. Protege los flujos críticos. Añade cobertura E2E para autenticación, pagos, navegación principal, permisos y otros flujos donde el comportamiento nativo pueda alterar el resultado.
  6. Mide las pantallas sensibles por separado. Usa comparaciones de rendimiento repetidas para listas, feeds y rutas de renderizado costosas en lugar de basarte en la duración de Jest.

Para mayor confianza en el release, conecta la suite a pruebas de integración CI/CD y requiere la capa de pruebas adecuada antes del despliegue. Si una corrección de JavaScript o de assets debe llegar a los usuarios tras su validación, Capgo puede entregar web bundles firmados a canales específicos para aplicaciones de CapacitorJS y Electron, con controles de rollout y protección contra rollback. Ese flujo de entrega no sustituye las pruebas de dispositivo en React Native, pero ilustra el mismo principio: validar el comportamiento en la capa donde se ejecuta.

La pregunta correcta no es si React Native Testing Library puede probar toda tu app. No puede, y ese límite oficial es útil. La pregunta correcta es si cada riesgo importante tiene una prueba ejecutándose en el entorno capaz de detectarlo.


Capgo conecta cambios validados de assets y JavaScript con una entrega controlada para equipos de CapacitorJS y Electron, mediante canales específicos, visibilidad de rollout y protección contra rollback. Visita Capgo para ver cómo puede integrarse en tu flujo de trabajo de pruebas de componentes, E2E y CI.

Live Updates para apps de Capacitor

Si hay un bug en la capa web, publica el fix con Capgo en lugar de esperar días a la aprobación de la app store. Los usuarios reciben la actualización en segundo plano mientras los cambios nativos siguen el proceso de revisión normal.

Lo último de nuestro blog

Capgo te da los mejores insights para crear una app móvil realmente profesional.