Automatización de apps móviles en dispositivos reales
Crea una automatización de apps móviles fiable en dispositivos reales con Appium, ADB, UIAutomator y XCUITest, además de soluciones a la inestabilidad, paralelización de la flota e integración con CI.
La automatización de apps móviles en dispositivos reales usa frameworks como Appium, ADB, UIAutomator y XCUITest para conducir teléfonos físicos a través de flujos repetibles. Ejecutar sobre hardware real —y paralelizar en una flota— te da una cobertura de regresión y una señal de rendimiento que los emuladores no pueden igualar.
- The core stacks are Appium (cross-platform), UIAutomator/Espresso and ADB (Android), and XCUITest (iOS); scrcpy is invaluable for observing and debugging Android runs.
- Prefer written, versioned scripts over record-and-playback for anything you will maintain; recording is fine for one-off exploration.
- Flakiness is the main enemy — solve it with explicit waits and stable selectors, not sleeps.
- A fleet's value is parallelization: shard suites across devices to keep wall-clock time low.
- Wire automation into CI so every build runs against a real-device core tier.
La pila de automatización
Appium
Appium es la opción multiplataforma más habitual. Expone el protocolo W3C WebDriver, de modo que escribes las pruebas en el lenguaje que prefieras y controlas Android e iOS a través de una única API. Por debajo, Appium delega en motores de automatización específicos de cada plataforma —UIAutomator2/Espresso en Android, XCUITest en iOS—, así que obtienes un control nativo con una interfaz portable. Es adecuado para equipos que quieren un solo framework y una sola base de pruebas en todas las plataformas.
ADB (Android Debug Bridge)
ADB es la navaja suiza de Android. Además de instalar y lanzar apps, ejecuta acciones a nivel de shell: conceder permisos, configurar red e idioma, simular eventos de entrada, capturar logcat, extraer capturas de pantalla, activar el modo avión y preparar condiciones para las pruebas. La mayor parte de la automatización en Android se apoya en ADB para la configuración inicial, el desmontaje y el control del estado del dispositivo, incluso cuando el control de la UI se realiza mediante otro framework.
UIAutomator y Espresso
Para equipos que solo trabajan con Android, Espresso (en proceso, de caja blanca, rápido y estable) cubre la UI de tu propia app, mientras que UIAutomator gestiona las interacciones entre apps y con la UI del sistema (notificaciones, diálogos de permisos, ajustes). Suelen usarse juntos.
XCUITest
XCUITest es el framework nativo de UI de Apple, que se ejecuta a través de Xcode. Es la vía más fiable para la automatización de UI en iOS y es lo que Appium usa por debajo en iOS. Para conocer el comportamiento específico de cada plataforma y las diferencias de configuración, consulta flotas de iOS frente a Android.
scrcpy
scrcpy refleja y controla un dispositivo Android por USB o TCP/IP con baja latencia. No es un framework de pruebas, pero es la forma más rápida de ver una ejecución automatizada, reproducir un fallo de forma interactiva y depurar selectores en directo.
| Appium (cross-platform) | Native (Espresso / XCUITest) | |
|---|---|---|
| Codebase | One test suite, both platforms | Separate suite per platform |
| Speed & stability | Good, one layer of indirection | Fastest, most stable on its platform |
| System UI access | Via UIAutomator delegation | Direct (UIAutomator/XCUITest) |
| Best fit | Small team, shared flows | Platform-specific team, max reliability |
Script frente a grabación
Las herramientas de grabación y reproducción generan un script capturando tus toques. Resultan atractivas para un primer borrador o una comprobación puntual, pero los scripts grabados tienden a ser frágiles: fijan coordenadas o selectores poco robustos y carecen de la estructura necesaria para mantenerse a escala.
Los scripts escritos a mano son la mejor opción para cualquier cosa duradera. Te permiten usar localizadores estables (IDs de accesibilidad, IDs de recursos), extraer pasos reutilizables, parametrizar datos, añadir aserciones y mantener todo bajo control de versiones junto con la app. Un camino intermedio práctico es grabar para explorar y luego reescribir los flujos útiles como código mantenible con selectores y esperas adecuados.
Combatir la inestabilidad y la sincronización
Las pruebas inestables destruyen la confianza en una suite. En dispositivos reales los tiempos son realmente variables —la latencia de red, las animaciones y la carga en segundo plano cambian—, así que la solución es una sincronización disciplinada.
- Nunca uses esperas fijas como mecanismo de espera principal. Sustitúyelas por esperas explícitas que consulten una condición (elemento presente, visible, habilitado) hasta un tiempo límite.
- Usa selectores estables. Prefiere los identificadores de accesibilidad y los IDs de recursos frente a XPath por texto o índice, que se rompen con cambios de copy y de diseño.
- Espera al estado de la app, no al reloj. Las condiciones de inactividad, las señales de quietud de red y los estados de los elementos son más fiables que adivinar una duración.
- Controla el entorno. Reinicia el estado de la app entre pruebas, siembra datos conocidos, desactiva las animaciones cuando sea posible y fija el idioma/zona horaria para que las ejecuciones sean deterministas.
- Pon en cuarentena y reintenta con criterio. Aísla las pruebas conocidas por su inestabilidad, añade reintentos acotados para los pasos genuinamente no deterministas y haz seguimiento de las tasas de fallo intermitente para corregir las causas raíz en lugar de ocultarlas.
Paralelización en una flota
Un solo dispositivo ejecuta las pruebas en serie; una flota las ejecuta en paralelo. Esta es la razón principal para automatizar sobre hardware físico a escala.
El fragmentado (sharding) divide el conjunto de pruebas entre dispositivos, de modo que el tiempo total baja de forma aproximadamente lineal con el número de dispositivos. Puedes fragmentar por prueba (repartiendo los casos de forma uniforme) o por celda de matriz (cada dispositivo se encarga de una combinación de SO/modelo de tus niveles de cobertura).
Requisitos prácticos para ejecuciones paralelas limpias:
| Aspecto | Enfoque |
|---|---|
| Direccionamiento de dispositivos | IDs/números de serie estables por dispositivo; un registro que asocia dispositivos con capacidades |
| Aislamiento | Una sesión por dispositivo; reinicio del estado de la app entre ejecuciones |
| Asignación | Un programador que cede dispositivos libres a los trabajos y evita la doble asignación |
| Fiabilidad | Comprobaciones de salud para que los dispositivos caídos o sin conexión se omitan en lugar de contar como fallo |
Coordinar cesiones, colas y escalonamiento es tarea de una capa de orquestación; consulta programación y orquestación. Mantener los dispositivos lo bastante saludables como para confiar en los resultados se trata en monitorización y salud de la flota.
Integración con CI
La automatización aporta valor cuando se ejecuta de forma automática. El patrón habitual:
- Compilar el artefacto de la app en CI.
- Aprovisionar un dispositivo de la flota mediante el programador (ceder un dispositivo del nivel principal que coincida con la celda de matriz objetivo).
- Instalar y ejecutar la suite en el dispositivo cedido.
- Recopilar artefactos —logs (logcat/syslog), capturas de pantalla o vídeo, y un informe de pruebas estructurado.
- Bloquear el pipeline según el nivel principal; ejecuta una cobertura más amplia con una cadencia nocturna o previa al lanzamiento para mantener la rapidez del feedback en los PR.
Mantén el aprovisionamiento de dispositivos detrás del programador en lugar de fijar números de serie en el pipeline, de modo que los trabajos de CI se encolen de forma ordenada cuando los dispositivos estén ocupados y nunca colisionen.
Regla general: bloquea cada build con una suite del nivel principal pequeña, rápida y estable. Deja la matriz amplia y lenta para las ejecuciones nocturnas o de lanzamiento. Esto mantiene ágil el feedback para los desarrolladores sin sacrificar cobertura.