Saltar al contenido principal
Móvil Tutorial Capacitor

React Native Testing Library: cómo testear apps correctamente

Domina React Native Testing Library: setup, queries, mocking y tips de 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 sigue reportando que al pulsar “Continue” no ocurre nada en un dispositivo real. El test del componente encontró el botón, llamó a su handler y vio la pantalla esperada en un entorno de JavaScript mockeado. Nunca verificó el prompt de permisos nativos, el comportamiento del teclado, la animación, la API de la plataforma o el stack de navegación real.

Esa brecha es donde los equipos se hacen 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 cada una puede exponer.

Tabla de contenidos

Por qué el testing centrado en usuario cambia todo

Un fallo de test común comienza antes de escribir el test. Un desarrollador inspecciona las props de un componente, accede a su estado o compara un snapshot grande porque esos detalles son fáciles de afirmar. El test pasa, 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 afirmación nunca describió lo que el usuario necesita ver.

React Native Testing Library toma el enfoque opuesto. Renderizas el componente, interactúas con él a través de un control visible y luego afirmas el resultado que un usuario puede observar. Ese enfoque coincide con Ejemplo centrado en el usuario de React Native Testing Library, así como las directrices de pruebas de React Native para mantener las pruebas cortas, centrar cada prueba en una sola cosa, separar los aspectos de la vista de la lógica de negocio y el estado, y preferir la salida visible o los helpers de accesibilidad a los detalles internos de implementación.

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

Test the behavior users depend on

Suppose a login form disables its submit button while a request runs. A brittle test might inspect disabled en una instancia de componente particular o afirmar que una variable de estado cambió. Una prueba más sólida pulsa el control accesible «Iniciar sesión», espera al indicador de carga y comprueba que aparece un mensaje de error o la pantalla de destino.

A la segunda prueba no le importa si el formulario usa estado local, un reducer, un hook personalizado o una implementación distinta del botón. Le importa que la aplicación comunique el resultado correcto.

Regla práctica: Si un usuario no puede observarlo, pregúntate si pertenece a una prueba de comportamiento de componente.

Las consultas basadas en etiquetas y roles de accesibilidad también obligan a tomar mejores code de producto. Una pantalla que expone etiquetas significativas es más fácil de usar con tecnología de asistencia y más fácil de probar. Esa conexión importa al evaluar el experiencia de usuario de la app, porque la testabilidad y la usabilidad suelen mejorar juntas.

Entiende qué no prueban los tests que pasan

Una prueba de componente centrada en el usuario puede demostrar que JavaScript renderiza la rama esperada y responde a una pulsación simulada. No puede demostrar que un prompt biométrico se abre correctamente, que una cámara nativa devuelve un resultado utilizable, o que un flujo de pago sobrevive al comportamiento de 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 las capas de prueba honestas. Usa RNTL para el comportamiento de componentes y reserva las pruebas basadas en dispositivo para flujos donde la integración nativa, la navegación real, los permisos, la autenticación, los pagos o la funcionalidad central de la app pueden cambiar el resultado.

Instalación y configuración que realmente funciona

La configuración moderna comienza con el paquete con scope:

@testing-library/react-native

El antiguo react-native-testing-library El nombre de paquete npm aún existe como un artefacto histórico, pero los proyectos actuales deberían usar el paquete con scope mantenido dentro de la familia Testing Library. El proyecto repositorio GitHub lo describe como utilidades de React Native para fomentar buenas prácticas de testing, y su historial de releases muestra que la librería ha seguido cambiando junto con React Native en lugar de permanecer como un helper estático.

Instala la librería con tu entorno de Jest existente. Los proyectos de Expo comúnmente usan el preset de Jest de Expo, mientras que los proyectos de React Native bare pueden usar el preset de React Native:

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

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

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

Para una aplicación React Native sin wrapper, usa la configuración de Jest de React Native correspondiente. Mantén el preset alineado con la versión del framework. Muchas fallas que parecen problemas de RNTL provienen de un renderer de React, una transformación de Babel o un preset de Jest desalineados.

Mantén los archivos de setup explícitos

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

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

Luego, refiérete a él desde Jest:

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

Si tu app usa React Navigation, Reanimated, gestión de gestos, contexto de safe-area 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 pasa por type checking puede ocultar nombres de query incorrectos, parámetros de navegación inválidos o estructuras de mock inseguras.

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 React Test Renderer obsoleto 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 verificar las versiones de tu app.

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

Infografía que muestra la ruta de instalación en cinco pasos para configurar el proyecto de React Native Testing Library.

APIs principales, queries y assertions explicadas

Las pruebas de RNTL se vuelven confiables 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 hacer que una prueba que pasa sea engañosa.

Comienza con render:

const screen = render(<LoginForm />);

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

Un gráfico de pirámide que muestra el orden de prioridad recomendado para las consultas en la automatización de pruebas de software.

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

Cada familia de consultas tiene un propósito distinto:

  • Presencia síncrona: Usa getByRole, getByText, or another getBy consulta cuando el elemento ya debería existir. La prueba falla inmediatamente si no es así.
  • Aparición asíncrona: Usa findByRole o findByText cuando la renderización o la interacción provoca una actualización asíncrona.
  • Comprobaciones 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 pruebas de eventos específicas, fireEvent.press es directo:

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

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

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

Una aserción de handler encaja en un componente cuyo contrato público es un callback de evento. Es débil como única evidencia de que el flujo de usuario funciona.

Prefiere assertions específicas en vez de snapshots

Las 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 comprobar la lógica interna:

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

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

El paquete Testing Library pertenece a la organización más amplia 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 repositorio del proyecto. Las convenciones de query compartidas ayudan en varias plataformas, pero no deciden 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 deteniéndose en el límite del componente renderizado en JavaScript. Los permisos nativos, los stacks de navegación reales, los teclados de los dispositivos, el timing de frames y el comportamiento de memoria necesitan 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 forma de la aplicación. Los componentes de presentación necesitan tests de comportamiento directos, los hooks necesitan inputs y outputs controlados, la navegación necesita un provider suficientemente realista y los módulos nativos necesitan mocks que sigan siendo claramente distintos de la verificación en un dispositivo real.

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

Componentes de presentación

Mantén un test de 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 como lo haría un usuario o un servicio de accesibilidad, y confirme el resultado visible o de callback que importa. No hagas mock de todos los componentes hijos por defecto. Haz mock de límites costosos o no relacionados solo cuando oculten el comportamiento que estás testeando.

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

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 red o de 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 real y un pequeño test navigator suele ser más valioso que hacer mock de cada método de navegación. Pulsa 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 responsabilidad es despachar una ruta tipada, pero no validará el registro de rutas, los parámetros ni el comportamiento de un navigator anidado.

Los datos asíncronos merecen la misma disciplina. Haz mock de la respuesta de API o del repositorio, renderiza la pantalla, verifica el estado de carga, resuelve la petición y luego verifica la salida 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 la plataforma son útiles para tests de JavaScript deterministas. No son evidencia 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 fallo en lugar de modelar solo el happy path.

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

El límite es práctico: simula la dependencia para probar tus decisiones en JavaScript y luego ejecuta una prueba en el dispositivo para confirmar su comportamiento real.

Debugging de tests flaky, CI y checks de performance

Una prueba inestable suele indicar tiempo no controlado, estado compartido o una aserción que compite con la interfaz. Identifica qué condición está presente antes de aumentar los timeouts. Un timeout más largo puede ocultar el problema de programación y hacer que la suite sea más lenta.

Usa findBy para elementos que se espera que aparezcan después de una actualización. Usa waitFor para condiciones de estado o llamadas simuladas. Si los temporizadores falsos están habilitados, aváncelos en el punto donde la interacción lo requiera y restaura los temporizadores reales después. Un act el warning significa que React observó una actualización fuera de su límite de interacción esperado. Corrige el await, interacción del usuario o limpieza de temporizadores en lugar de suprimir la advertencia.

Haz que los fallos de CI sean reproducibles

Un trabajo de CI confiable instala desde el lockfile, ejecuta el mismo comando de Jest usado localmente y aísla el estado de los mocks. Limpia las llamadas a los mocks entre pruebas, resetea los módulos cuando el estado a nivel de módulo afecta el comportamiento y elimina las dependencias del orden de ejecución. La caché de Jest acelera el feedback, pero los cambios en dependencias o configuración requieren invalidar la caché correspondiente.

Los fallos solo en CI requieren una comparación de entornos antes de reescribir un componente. Revisa Node, el gestor de paquetes, la configuración de workers de Jest, la configuración de temporizadores y las variables de entorno. Reproduce el mismo comando localmente cuando sea posible y luego reduce la prueba fallida a la interacción más pequeña que exponga la diferencia.

La observabilidad cubre otro hueco distinto. Monitoreo de salud de la app proporciona contexto de errores de producción que las pruebas de componentes simulados no pueden reproducir, incluyendo fallos vinculados a dispositivos reales e integraciones nativas.

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

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

Una prueba funcional puede confirmar que una lista se renderiza. No puede determinar de forma fiable si un refactor cambió la duración o el número de renders, porque el tiempo de ejecución de la prueba añade ruido. Reassure mide esos valores para un escenario, repite el escenario para reducir la varianza y aplica análisis estadístico antes de informar un cambio significativo. Su documentación de pruebas de rendimiento también cubre informes adecuados para CI y revisión de pull requests.

Evita aserciones fijas como «este render debe terminar por debajo de un umbral elegido». Un worker de CI ocupado puede provocar un fallo falso, mientras que un umbral laxo puede pasar por alto una regresión real. Usa mediciones repetidas para señales de regresión e inspecciona el perfil del componente y del dispositivo cuando la comparación muestre una diferencia significativa.

Regla de medición: Las pruebas funcionales responden si el comportamiento es correcto. Las herramientas de rendimiento responden si un escenario medido cambió. Mantén esas preguntas separadas.

Poniéndolo todo junto y avanzando

React Native Testing Library pertenece a la capa amplia y rápida de tu estrategia de pruebas. Debe cubrir el comportamiento de componentes, cambios de estado visibles, resultados de accesibilidad, validación, estados de datos simulados y la integración entre componentes de JavaScript. Mantén esas pruebas enfocadas en lo que un usuario puede observar, y haz que los fallos apunten a un comportamiento específico en lugar de a un árbol renderizado grande.

La capa más pequeña basada en dispositivos debe proteger los flujos donde los mocks pueden mentir. El resumen de pruebas indica que RNTL no ofrece un runtime completo de React Native y no puede probar funciones nativas. La misma guía recomienda combinar 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 hace falta reescribir toda tu suite de tests de una sola vez.

  1. Conserva los tests de negocio valiosos. Mueve el estado puro y la lógica de dominio a unit tests enfocados donde den un feedback claro.
  2. Reemplaza primero 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 tus snapshots. Conserva solo los snapshots que los revisores puedan entender y mantener.
  4. Añade cobertura de límites nativos. Para cada módulo mockeado importante, identifica qué comportamiento del dispositivo aún requiere validación.
  5. Protege los flujos críticos. Añade cobertura E2E para autenticación, pago, navegación principal, permisos y otros flujos en los que el comportamiento nativo puede cambiar 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 adivinar por la duración de Jest.

Para tener confianza en el release, conecta la suite con pruebas de integración en CI/CD y requiere la capa de pruebas adecuada antes del despliegue. Si un fix 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 Capacitor

Cuando un bug de la capa web está en producción, publica el fix a través de 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 flujo de revisión habitual.

Lo último de nuestro blog

Capgo te da la información clave que necesitas para crear una app móvil realmente profesional.