Jest se ejecuta en Node - no hay cámara, GPS o Keychain. jest-expo proporciona stubs para la mayoría de los módulos del Expo SDK con valores por defecto seguros; añades mocks manuales cuando tus tests necesitan resultados de permisos específicos, lecturas de sensores o comportamiento de SDKs nativos de terceros.
Preset jest-expo instala mocks por defecto para módulos Expo para que las importaciones no causen crash en Jest. Los valores por defecto devuelven promesas resueltas con formas de "éxito" genéricas - raramente coincidiendo con tus casos edge de producción.
jest.mock("module-name") se levanta hacia la parte superior del archivo e intercambia el módulo antes de que tus importaciones se evalúen. Funciona para expo-*, @react-native-async-storage/async-storage y módulos locales de la app.
Mocks manuales en __mocks__/ junto al nombre de paquete de node_modules o archivo de src/ se aplican cuando llamas jest.mock sin una factory.
Patrón spy - jest.spyOn(module, "fn") envuelve una sola función mientras deja el resto real (cuando no está completamente mockeado).
Mockeo de límite - mockea src/services/api.ts, no fetch ni cada importación transitiva, a menos que estés testeando el cliente HTTP en sí.
Trata los mocks nativos de Jest como tests de contrato para tu reacción JS - no como prueba de que el bridge nativo funciona. Añade Maestro E2E o QA manual para diálogos de permisos y rutas de hardware.
Asumir que los valores por defecto de jest-expo coinciden con tu test - Los mocks de permisos por defecto frecuentemente devuelven granted. Los tests nunca ejercen rutas de denegación a menos que sobrescribas explícitamente. Solución: Establece mockResolvedValue para cada escenario de permiso/estado explícitamente.
Mockear demasiado profundo - Mockear internals de react-native o cada importación de expo-* hace tests frágiles cuando cambios del SDK alteran firmas. Solución: Mockea tu límite de servicio; mantén un módulo responsable de llamadas nativas.
Olvidar jest.clearAllMocks() - Los conteos de llamadas e implementaciones encoladas se filtran entre tests. Solución:afterEach en jest.setup.ts o beforeEach en bloques describe que usan spies.
jest.requireActual después de mock completo - Esparcir requireActual para módulos nativos puede traer bindings nativos reales a Jest y causar crash. Solución: Mockea solo las funciones que llamas; evita requireActual en módulos que tocan código nativo.
Falsa confianza de suites verdes - Todo comportamiento nativo es fake; la producción aún falla en un dispositivo real. Solución: Documenta qué flujos necesitan Maestro o chequeos de dispositivo manual en plantillas de PR.
import() dinámico eludiendo mocks - Las importaciones lazy pueden resolverse antes de que jest.mock se aplique si están mal ordenadas. Solución: Usa importaciones estáticas en código bajo test, o jest.unstable_mockModule para rutas de importación dinámica ESM.
Duplicar configuración de mock en cada archivo - Los mocks de expo-font copiados-pegados derivan. Solución: Helpers compartidos en test-utils/native-mocks.ts o mocks globales en jest.setup.ts para módulos de app-wide (fonts, pantalla de splash).
Mockea la mayoría de paquetes expo-* enviados con el SDK. Módulos nativos personalizados, SDKs bare de terceros y algunos paquetes de comunidad no están cubiertos - añade jest.mock manualmente.
¿Dónde coloco mocks compartidos?
A nivel de app: jest.setup.ts (fonts, pantalla de splash, constantes).
Específico de feature: parte superior del archivo de test o __tests__/helpers/.
Estilo de paquete: __mocks__/expo-location.ts en raíz del proyecto para auto-resolución de jest.mock("expo-location").
¿jest.mock vs jest.spyOn?
jest.mock reemplaza el módulo entero - úsalo para bridges nativos.
jest.spyOn envuelve un método - úsalo cuando la mayoría del módulo debe mantenerse real (raro con módulos nativos en Jest).
El paquete envía un mock Jest oficial - prefierelo sobre objetos clave-valor hechos a mano.
¿Cómo mockeo expo-router?
Prefiere renderRouter desde expo-router/testing-library sobre mockear useRouter cuando testeas navegación. Para componentes hoja, pasa callbacks de navegación como props en lugar de mockear el hook del router.
Úsalo raramente - requireActual en módulos nativos pesados puede fallar; un módulo wrapper delgado es más seguro.
¿Por qué mi mock no se aplica?
jest.mock debe estar en scope de módulo (levantado), no dentro de it/beforeEach.
Asegúrate de que la ruta de importación coincide exactamente con lo que el código de producción importa.
Limpia caché de Metro/Jest: npx jest --clearCache.
¿Debo mockear fetch o el cliente API?
Mockea src/api/client o el wrapper de queryFn de TanStack Query - no ambos. Un límite mantiene los tests enfocados en cómo la app maneja respuestas de éxito y error.
Coincide con la forma desde los tipos TypeScript del módulo Expo real - objetos incompletos causan falsos positivos.
¿Qué es falsa confianza?
Los tests pasan porque los mocks siempre devuelven éxito, mientras los usuarios reales golpean diálogos de denegación, modo avión o bugs del OS. Complementa Jest con Maestro E2E para rutas de permisos e integración del OS.
¿Cómo testeo un módulo nativo Expo personalizado?
Crea una fachada TypeScript que importe tu módulo. En Jest, jest.mock("@/modules/my-native-module") con un mock manual devolviendo la API JS que tu app espera. Valida el módulo real en compilaciones de dispositivo.
¿Pertenecen los mocks en setupFilesAfterEnv?
Solo para módulos que cada test necesita (fonts, splash, reanimated silence). Los mocks de feature pertenecen junto a los tests que afirman sobre ellos - los mocks globales ocultan setup faltante en tests nuevos.
¿Cómo reinicio mocks entre tests?
afterEach(() => { jest.clearAllMocks(); // limpia historial de llamadas // jest.resetAllMocks(); // también reinicia implementaciones - úsalo cuando tests sobrescriben mockResolvedValue});
Añade a jest.setup.ts para consistencia a nivel de proyecto.