TL;DR
- Appium automatiza apps iOS y Android con protocolo WebDriver — un framework, ambas plataformas
- Setup: servidor Appium, SDKs de plataforma (Android Studio/Xcode), biblioteca cliente
- Encuentra elementos por accessibility id, xpath o locators específicos de plataforma
- Soporta gestos (swipe, scroll, tap), dispositivos reales y emuladores/simuladores
- Integra con CI/CD vía Appium en Docker o servicios cloud (BrowserStack, Sauce Labs)
Ideal para: Equipos QA testeando apps móviles cross-platform Omite si: Testeas solo Android (usa Espresso) o solo iOS (usa XCUITest) Tiempo de lectura: 20 minutos
Tu app funciona en iOS. Se cae en Android. O viceversa. Testing manual en ambas plataformas duplica el esfuerzo. La paridad de features se vuelve una pesadilla.
Appium resuelve el testing móvil cross-platform. Escribe un test, corre en iOS y Android. Mismo lenguaje, mismo framework, mismo pipeline CI.
Este tutorial cubre Appium desde setup hasta integración CI/CD — todo lo que necesitas para automatizar apps móviles.
¿Qué es Appium?
Appium es un framework open-source para automatización móvil. Automatiza apps nativas, híbridas y web móviles en iOS y Android usando el protocolo WebDriver.
Cómo funciona Appium:
- Tu código de test envía comandos al servidor Appium
- Appium traduce comandos a acciones específicas de plataforma
- UIAutomator2 (Android) o XCUITest (iOS) ejecuta las acciones
- Los resultados regresan por la misma cadena
Por qué Appium:
- Cross-platform — mismos tests para iOS y Android
- Agnóstico de lenguaje — Java, Python, JavaScript, Ruby, C#
- Sin modificación de app — testea builds de producción reales
- Estándar WebDriver — API familiar para usuarios de Selenium
- Open-source — gratis, comunidad activa, actualizaciones regulares
Configuración del Entorno
Prerrequisitos
Para Android:
- Java JDK 11+
- Android Studio con SDK
- Variable de entorno
ANDROID_HOME - Depuración USB habilitada en dispositivo
Para iOS (solo macOS):
- Xcode con Command Line Tools
- Simulador iOS o dispositivo real
- Cuenta Apple Developer (para dispositivos reales)
Instalando Appium
# Instalar Appium 2.x globalmente
npm install -g appium
# Verificar instalación
appium --version
# Instalar drivers de plataforma
appium driver install uiautomator2 # Android
appium driver install xcuitest # iOS
# Listar drivers instalados
appium driver list --installed
Appium Inspector
Instala Appium Inspector para inspección de elementos:
- Descarga de GitHub releases
- O usa versión web en inspector.appiumpro.com
Desired Capabilities
Capabilities le dicen a Appium qué dispositivo y app usar.
Android Capabilities
from appium import webdriver
from appium.options.android import UiAutomator2Options
options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = "Pixel 6"
options.app = "/path/to/app.apk"
options.automation_name = "UiAutomator2"
# Opcional pero recomendado
options.no_reset = True # No resetear estado de app
options.full_reset = False
options.new_command_timeout = 300
driver = webdriver.Remote("http://localhost:4723", options=options)
iOS Capabilities
from appium import webdriver
from appium.options.ios import XCUITestOptions
options = XCUITestOptions()
options.platform_name = "iOS"
options.device_name = "iPhone 14"
options.platform_version = "16.0"
options.app = "/path/to/app.app"
options.automation_name = "XCUITest"
# Para dispositivos reales
options.udid = "device-udid-here"
driver = webdriver.Remote("http://localhost:4723", options=options)
Testeando Apps Instaladas
# Android - usa app package y activity
options.app_package = "com.example.myapp"
options.app_activity = "com.example.myapp.MainActivity"
# iOS - usa bundle ID
options.bundle_id = "com.example.myapp"
Encontrando Elementos
Estrategias de Locators
from appium.webdriver.common.appiumby import AppiumBy
# Accessibility ID (recomendado - funciona cross-platform)
element = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "login_button")
# ID (Android resource-id)
element = driver.find_element(AppiumBy.ID, "com.example:id/login_button")
# XPath (más lento, pero flexible)
element = driver.find_element(AppiumBy.XPATH, "//android.widget.Button[@text='Login']")
# Class name
element = driver.find_element(AppiumBy.CLASS_NAME, "android.widget.EditText")
# Android UIAutomator
element = driver.find_element(
AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().text("Login")'
)
# iOS predicate string
element = driver.find_element(
AppiumBy.IOS_PREDICATE,
'label == "Login" AND type == "XCUIElementTypeButton"'
)
# iOS class chain
element = driver.find_element(
AppiumBy.IOS_CLASS_CHAIN,
'**/XCUIElementTypeButton[`label == "Login"`]'
)
Esperando Elementos
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
# Esperar que elemento sea visible
element = wait.until(
EC.visibility_of_element_located((AppiumBy.ACCESSIBILITY_ID, "welcome_message"))
)
# Esperar que elemento sea clickeable
element = wait.until(
EC.element_to_be_clickable((AppiumBy.ACCESSIBILITY_ID, "submit_button"))
)
# Esperar que elemento desaparezca
wait.until(
EC.invisibility_of_element_located((AppiumBy.ACCESSIBILITY_ID, "loading_spinner"))
)
Interacciones Básicas
Tap, Escribir, Limpiar
# Tap/Click
login_button = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "login_button")
login_button.click()
# Escribir texto
username_field = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "username")
username_field.send_keys("testuser")
# Limpiar campo
username_field.clear()
# Obtener texto
message = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "message")
print(message.text)
# Verificar si está mostrado/habilitado
assert login_button.is_displayed()
assert login_button.is_enabled()
Manejando Teclado
# Ocultar teclado
driver.hide_keyboard()
# Verificar si teclado está mostrado (Android)
is_keyboard_shown = driver.is_keyboard_shown()
Gestos
Swipe y Scroll
from appium.webdriver.common.touch_action import TouchAction
# Swipe simple (start_x, start_y, end_x, end_y, duration_ms)
driver.swipe(500, 1500, 500, 500, 800)
# Scroll a elemento (Android)
driver.find_element(
AppiumBy.ANDROID_UIAUTOMATOR,
'new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().text("Target Text"))'
)
# Scroll por dirección
def scroll_down(driver):
size = driver.get_window_size()
start_x = size['width'] // 2
start_y = size['height'] * 0.8
end_y = size['height'] * 0.2
driver.swipe(start_x, start_y, start_x, end_y, 500)
def scroll_up(driver):
size = driver.get_window_size()
start_x = size['width'] // 2
start_y = size['height'] * 0.2
end_y = size['height'] * 0.8
driver.swipe(start_x, start_y, start_x, end_y, 500)
W3C Actions (Recomendado)
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.actions import interaction
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.pointer_input import PointerInput
# Tap en coordenadas
actions = ActionChains(driver)
actions.w3c_actions = ActionBuilder(driver, mouse=PointerInput(interaction.POINTER_TOUCH, "touch"))
actions.w3c_actions.pointer_action.move_to_location(100, 200)
actions.w3c_actions.pointer_action.pointer_down()
actions.w3c_actions.pointer_action.pause(0.1)
actions.w3c_actions.pointer_action.release()
actions.perform()
# Presión larga
element = driver.find_element(AppiumBy.ACCESSIBILITY_ID, "item")
actions = ActionChains(driver)
actions.click_and_hold(element).pause(2).release().perform()
Ejemplo Completo de Test
import pytest
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class TestLoginFlow:
@pytest.fixture(autouse=True)
def setup(self):
options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = "emulator-5554"
options.app = "./app-debug.apk"
options.automation_name = "UiAutomator2"
options.no_reset = False
self.driver = webdriver.Remote("http://localhost:4723", options=options)
self.wait = WebDriverWait(self.driver, 15)
yield
self.driver.quit()
def test_successful_login(self):
# Ingresar username
username = self.wait.until(
EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, "username_input"))
)
username.send_keys("testuser@example.com")
# Ingresar password
password = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "password_input")
password.send_keys("securepassword123")
# Ocultar teclado
self.driver.hide_keyboard()
# Tap en botón login
login_btn = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "login_button")
login_btn.click()
# Verificar mensaje de bienvenida
welcome = self.wait.until(
EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, "welcome_message"))
)
assert "Welcome" in welcome.text
def test_invalid_credentials(self):
username = self.wait.until(
EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, "username_input"))
)
username.send_keys("wrong@example.com")
password = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "password_input")
password.send_keys("wrongpassword")
self.driver.hide_keyboard()
login_btn = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "login_button")
login_btn.click()
# Verificar mensaje de error
error = self.wait.until(
EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, "error_message"))
)
assert "Invalid credentials" in error.text
Patrón Page Object
# pages/base_page.py
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class BasePage:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 15)
def find(self, locator):
return self.wait.until(EC.presence_of_element_located(locator))
def click(self, locator):
self.find(locator).click()
def type_text(self, locator, text):
element = self.find(locator)
element.clear()
element.send_keys(text)
# pages/login_page.py
class LoginPage(BasePage):
USERNAME = (AppiumBy.ACCESSIBILITY_ID, "username_input")
PASSWORD = (AppiumBy.ACCESSIBILITY_ID, "password_input")
LOGIN_BTN = (AppiumBy.ACCESSIBILITY_ID, "login_button")
ERROR_MSG = (AppiumBy.ACCESSIBILITY_ID, "error_message")
def login(self, username, password):
self.type_text(self.USERNAME, username)
self.type_text(self.PASSWORD, password)
self.driver.hide_keyboard()
self.click(self.LOGIN_BTN)
def get_error_message(self):
return self.find(self.ERROR_MSG).text
# tests/test_login.py
class TestLogin:
def test_login_success(self, driver):
login_page = LoginPage(driver)
login_page.login("user@example.com", "password123")
# assertions...
Testing en Dispositivos Reales
Dispositivo Android Real
# Conectar dispositivo por USB
adb devices
# Obtener nombre del dispositivo
adb shell getprop ro.product.model
# Habilitar depuración USB en opciones de desarrollador
options.device_name = "Pixel 6"
options.udid = "emulator-5554" # o serial real del dispositivo
Dispositivo iOS Real
Requiere provisioning profile y firma:
options.udid = "00008030-001234567890"
options.xcode_org_id = "TEAM_ID"
options.xcode_signing_id = "iPhone Developer"
Granjas de Dispositivos Cloud
BrowserStack:
from appium import webdriver
options = {
"platformName": "Android",
"appium:deviceName": "Samsung Galaxy S23",
"appium:platformVersion": "13.0",
"appium:app": "bs://app-id",
"bstack:options": {
"userName": "YOUR_USERNAME",
"accessKey": "YOUR_ACCESS_KEY",
"projectName": "My Project",
"buildName": "Build 1.0"
}
}
driver = webdriver.Remote(
"https://hub-cloud.browserstack.com/wd/hub",
options=options
)
Integración CI/CD
GitHub Actions
name: Mobile Tests
on: [push, pull_request]
jobs:
android-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install appium-python-client pytest
npm install -g appium
appium driver install uiautomator2
- name: Start Android emulator
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 33
script: |
appium &
sleep 10
pytest tests/android/ --junitxml=results.xml
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: test-results
path: results.xml
Appium con Asistencia de IA
Las herramientas de IA pueden ayudar a escribir y depurar tests de Appium.
Lo que la IA hace bien:
- Generar page objects desde screenshots de la app
- Sugerir estrategias de locators
- Depurar errores de elementos no encontrados
- Convertir tests entre lenguajes
Lo que aún necesita humanos:
- Entender lógica de negocio de la app
- Elegir entre locators estables e inestables
- Optimizar tiempo de ejecución de tests
- Manejar edge cases específicos de plataforma
FAQ
¿Qué es Appium?
Appium es un framework open-source para automatización móvil de testing de aplicaciones iOS y Android. Usa el protocolo WebDriver, permitiendo escribir tests en cualquier lenguaje que tenga cliente WebDriver (Java, Python, JavaScript, Ruby, C#). Appium puede testear apps nativas, híbridas y web móviles sin requerir modificación — testeas builds reales de producción.
¿Appium es gratis?
Sí, Appium es completamente gratis y open-source bajo licencia Apache 2.0. No hay ediciones enterprise, funciones premium ni límites de uso. Todo el framework, incluyendo todos los drivers (UiAutomator2, XCUITest), está disponible sin costo.
¿Appium vs Espresso/XCUITest — cuál es mejor?
Appium sobresale en testing cross-platform — escribe tests una vez, corre en iOS y Android. Espresso (Android) y XCUITest (iOS) son específicos de plataforma pero más rápidos, estables y con integración más profunda con el OS. Usa Appium cuando necesites cobertura cross-platform con un código. Usa herramientas nativas cuando necesites máxima velocidad y confiabilidad para una plataforma.
¿Puede Appium testear iOS y Android?
Sí, esta es la ventaja principal de Appium. El mismo código de test puede correr en ambas plataformas con cambios mínimos — usualmente solo locators diferentes para elementos específicos de plataforma. Escribes tests en tu lenguaje preferido, y Appium traduce comandos a la automatización de plataforma apropiada (UiAutomator2 para Android, XCUITest para iOS).
Recursos Oficiales
Ver También
- API Testing Guide - Fundamentos completos de API testing
- Selenium Tutorial - Fundamentos de automatización web
- Playwright Tutorial - Framework moderno de testing web
- CI/CD Testing Guide - Estrategias de integración continua
