Introducción a las Pruebas de Accesibilidad Móvil

Las pruebas de accesibilidad móvil garantizan que las aplicaciones sean utilizables por todos, incluidas las personas con discapacidades visuales, auditivas, motoras o cognitivas. Con más de mil millones de personas en todo el mundo que viven con algún tipo de discapacidad, la accesibilidad no es solo un requisito legal, es un aspecto fundamental del diseño inclusivo que amplía su base de usuarios (como se discute en Cross-Platform Mobile Testing: Strategies for Multi-Device Success) (como se discute en Appium 2.0: New Architecture and Cloud Integration for Modern Mobile Testing) y mejora la experiencia de usuario para todos.

Las pruebas de accesibilidad móvil van más allá de las pruebas básicas de funcionalidad. Requieren comprender cómo las tecnologías de asistencia interactúan con su aplicación, garantizar el cumplimiento de las Pautas de Accesibilidad al Contenido Web (WCAG) y validar que los usuarios (como se discute en Detox: Grey-Box Testing for React Native Applications) con discapacidades puedan completar todos los recorridos críticos de usuario de forma independiente.

Las consecuencias son importantes: las aplicaciones inaccesibles pueden enfrentar acciones legales bajo la Ley de Estadounidenses con Discapacidades (ADA), la Sección 508 y regulaciones similares en todo el mundo. Más importante aún, las aplicaciones inaccesibles excluyen a millones de usuarios potenciales y dañan la reputación de la marca.

Directrices WCAG 2.1 y 2.2 para Móviles

Las Pautas de Accesibilidad al Contenido Web (WCAG) 2.1 introdujeron criterios de éxito específicos para móviles, con WCAG 2.2 añadiendo refinamientos adicionales. Estas directrices se organizan en torno a cuatro principios: Perceptible, Operable, Comprensible y Robusto (POUR).

Criterios de Éxito WCAG Clave para Móviles

CriterioNivelEnfoque Móvil
1.3.4 OrientaciónAASoportar modos vertical y horizontal
1.3.5 Identificar Propósito de EntradaAAIdentificar programáticamente propósitos de campos
1.4.10 ReflujoAAContenido adaptable sin desplazamiento horizontal
1.4.11 Contraste No TextualAAContraste 3:1 para componentes UI y gráficos
1.4.12 Espaciado de TextoAASoportar preferencias de espaciado de usuario
1.4.13 Contenido al Pasar/EnfocarAAContenido descartable, sobre el que se puede pasar y persistente
2.5.1 Gestos de PunteroAAlternativas para gestos multipunto o basados en trayectoria
2.5.2 Cancelación de PunteroACapacidad de abortar o deshacer acciones táctiles
2.5.3 Etiqueta en el NombreAEtiquetas visuales coinciden con nombres programáticos
2.5.4 Activación por MovimientoAAlternativas para entrada basada en movimiento
2.5.5 Tamaño del ObjetivoAAAMínimo 44x44 píxeles CSS para objetivos táctiles
2.5.7 Movimientos de ArrastreAAAlternativas de puntero único para arrastrar
2.5.8 Tamaño del Objetivo (Mínimo)AAAl menos 24x24 píxeles CSS para objetivos

Niveles de Conformidad

  • Nivel A: Características básicas de accesibilidad (requisito mínimo)
  • Nivel AA: Estándar de la industria y requisito legal para la mayoría de jurisdicciones
  • Nivel AAA: Accesibilidad mejorada (estándar de oro)

La mayoría de las organizaciones apuntan al cumplimiento WCAG 2.1 Nivel AA, que cubre la mayoría de las necesidades de accesibilidad sin ser prohibitivamente difícil de lograr.

Pruebas con VoiceOver en iOS

VoiceOver es el lector de pantalla integrado de Apple para iOS y iPadOS. Probar con VoiceOver garantiza que los usuarios con discapacidad visual puedan navegar e interactuar con su aplicación de manera efectiva.

Habilitar VoiceOver

Habilitación Rápida: Triple clic en el botón lateral (configurar en Ajustes > Accesibilidad > Atajo de Accesibilidad)

Habilitación Manual: Ajustes > Accesibilidad > VoiceOver > Activar

Gestos Esenciales de VoiceOver

Toque Simple              - Seleccionar y hablar elemento
Doble Toque               - Activar elemento seleccionado
Deslizar Derecha          - Siguiente elemento
Deslizar Izquierda        - Elemento anterior
Toque con Dos Dedos       - Pausar/reanudar VoiceOver
Deslizar Arriba (3 dedos) - Desplazar hacia abajo
Deslizar Abajo (3 dedos)  - Desplazar hacia arriba
Gesto de Rotor            - Dos dedos rotando en pantalla

Lista de Verificación de Pruebas VoiceOver

1. Pruebas de Flujo de Navegación

// Ejemplo de etiqueta de accesibilidad adecuada
button.accessibilityLabel = "Agregar al carrito"
button.accessibilityHint = "Agrega el artículo a tu carrito de compras"
button.accessibilityTraits = .button

// Mal ejemplo - carece de contexto
button.accessibilityLabel = "Agregar" // Demasiado vago

2. Pruebas de Navegación con Rotor

El rotor de VoiceOver permite a los usuarios navegar por encabezados, enlaces, controles de formulario y más. Pruebe que:

  • Los encabezados estén correctamente etiquetados con accessibilityTraits = .header
  • Los campos de formulario estén agrupados lógicamente
  • Las acciones personalizadas del rotor funcionen como se espera
// Crear rotor personalizado para secciones de tabla
let customRotor = UIAccessibilityCustomRotor(name: "Secciones") { predicate in
    // Lógica de navegación personalizada
    return UIAccessibilityCustomRotorItemResult(
        targetElement: nextSection,
        targetRange: nil
    )
}
accessibilityCustomRotors = [customRotor]

3. Pruebas de Contenido Dinámico

Cuando el contenido se actualiza, los usuarios de VoiceOver necesitan notificaciones:

// Anunciar cambios a usuarios de lector de pantalla
UIAccessibility.post(
    notification: .announcement,
    argument: "Carrito actualizado con 3 artículos"
)

// Notificar cambios de diseño
UIAccessibility.post(
    notification: .layoutChanged,
    argument: firstNewElement
)

4. Texto Alternativo para Imágenes

// Imágenes decorativas
decorativeImage.isAccessibilityElement = false

// Imágenes informativas
productImage.accessibilityLabel = "Zapatos deportivos azules, talla 10"

// Imágenes complejas (gráficos, diagramas)
chartView.accessibilityLabel = "Gráfico de ingresos"
chartView.accessibilityValue = "T1: $50K, T2: $75K, T3: $100K, T4: $125K"

Pruebas con TalkBack en Android

TalkBack es el lector de pantalla de Google para dispositivos Android. Proporciona retroalimentación hablada y permite a los usuarios navegar aplicaciones usando gestos y teclados externos.

Habilitar TalkBack

Habilitación Rápida: Atajo de teclas de volumen (configurar en Ajustes > Accesibilidad > TalkBack > Atajo de TalkBack)

Habilitación Manual: Ajustes > Accesibilidad > TalkBack > Activar

Gestos Esenciales de TalkBack

Toque Simple              - Explorar por tacto
Doble Toque               - Activar elemento seleccionado
Deslizar Derecha          - Siguiente elemento
Deslizar Izquierda        - Elemento anterior
Deslizar Abajo-Derecha    - Leer desde arriba
Deslizar Arriba-Derecha   - Leer desde posición actual
Deslizar Izquierda-Derecha - Navegación atrás
Deslizar Derecha-Izquierda - Inicio

Implementación de Pruebas TalkBack

1. Descripciones de Contenido

// Botón con descripción clara
addButton.contentDescription = "Agregar artículo al carrito"

// ImageView con descripción significativa
productImage.contentDescription = "Billetera de cuero roja con cremallera dorada"

// Imágenes decorativas - ocultar de TalkBack
decorativeIcon.importantForAccessibility =
    View.IMPORTANT_FOR_ACCESSIBILITY_NO

// Agrupar elementos relacionados
containerLayout.contentDescription = "Tarjeta de perfil de usuario"
containerLayout.importantForAccessibility =
    View.IMPORTANT_FOR_ACCESSIBILITY_YES

2. Acciones Personalizadas

Permitir a los usuarios realizar múltiples acciones en un solo elemento:

ViewCompat.addAccessibilityAction(
    emailItem,
    "Eliminar correo"
) { view, arguments ->
    deleteEmail(view)
    true
}

ViewCompat.addAccessibilityAction(
    emailItem,
    "Marcar como leído"
) { view, arguments ->
    markAsRead(view)
    true
}

3. Anuncios de Región Activa

// Anunciar cambios de contenido dinámico
statusTextView.accessibilityLiveRegion =
    View.ACCESSIBILITY_LIVE_REGION_POLITE

// Para anuncios urgentes
errorTextView.accessibilityLiveRegion =
    View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE

// Anuncio programático
view.announceForAccessibility("Carga completa")

4. Navegación por Encabezados

// Marcar vista como encabezado para navegación
titleTextView.accessibilityHeading = true

// O usando ViewCompat
ViewCompat.setAccessibilityHeading(titleTextView, true)

Etiquetas y Sugerencias de Accesibilidad

Las etiquetas y sugerencias de accesibilidad efectivas son cruciales para que los usuarios de lectores de pantalla comprendan los elementos de la interfaz.

Mejores Prácticas de Etiqueta vs. Sugerencia

Etiquetas de Accesibilidad: Qué es el elemento Sugerencias de Accesibilidad: Qué hace el elemento cuando se activa

// Ejemplo iOS
saveButton.accessibilityLabel = "Guardar documento"
saveButton.accessibilityHint = "Guarda tu documento en almacenamiento en la nube"

// Ejemplo Android
saveButton.contentDescription = "Guardar documento"
saveButton.tooltipText = "Guarda tu documento en almacenamiento en la nube"

Directrices de Etiquetas

Buena PrácticaMala Práctica
“Buscar productos”“Buscar” (carece de contexto)
“Foto de perfil”“Imagen” (genérico)
“Calificación 5 estrellas”“Estrellas” (falta valor)
“Cerrar diálogo”“X” (acción poco clara)
“Volumen: 75%”“Deslizador” (falta estado)

Etiquetas Sensibles al Contexto

// Etiquetas dinámicas según estado
fun updatePlayButtonLabel() {
    playButton.contentDescription = when (mediaPlayer.isPlaying) {
        true -> "Pausar audio"
        false -> "Reproducir audio"
    }
}

// Carrito de compras con conteo de artículos
fun updateCartLabel(itemCount: Int) {
    cartButton.contentDescription = when (itemCount) {
        0 -> "Carrito de compras, vacío"
        1 -> "Carrito de compras, 1 artículo"
        else -> "Carrito de compras, $itemCount artículos"
    }
}

Contraste de Color y Diseño Visual

WCAG requiere contraste de color suficiente para garantizar que el texto y los elementos de UI sean perceptibles por usuarios con discapacidades visuales.

Requisitos de Contraste

Tipo de ContenidoWCAG Nivel AAWCAG Nivel AAA
Texto normal (< 18pt)4.5:17:1
Texto grande (≥ 18pt o ≥ 14pt negrita)3:14.5:1
Componentes UI y gráficos3:1N/A
Elementos deshabilitados/inactivosSin requisitoSin requisito

Probar Contraste de Color

// iOS - Soportar Dynamic Type con contraste adecuado
extension UIColor {
    static func accessibleText(on background: UIColor) -> UIColor {
        // Calcular luminancia y devolver color de texto apropiado
        let backgroundLuminance = background.luminance()
        return backgroundLuminance > 0.5 ? .black : .white
    }

    func contrastRatio(with color: UIColor) -> CGFloat {
        let luminance1 = self.luminance()
        let luminance2 = color.luminance()
        let lighter = max(luminance1, luminance2)
        let darker = min(luminance1, luminance2)
        return (lighter + 0.05) / (darker + 0.05)
    }
}

Independencia del Color

Nunca confíe únicamente en el color para transmitir información:

// Mal: Solo el color indica estado
statusIndicator.setBackgroundColor(Color.RED) // Estado de error

// Bien: Color + icono + texto
statusIndicator.apply {
    setBackgroundColor(Color.RED)
    setImageResource(R.drawable.ic_error)
    contentDescription = "Error: Conexión fallida"
}

// Bien: Patrón + color para gráficos
chartView.apply {
    // Usar diferentes patrones/texturas para series de datos
    series1.pattern = Pattern.SOLID
    series2.pattern = Pattern.STRIPED
    series3.pattern = Pattern.DOTTED
}

Dimensionamiento y Espaciado de Objetivos Táctiles

Los tamaños adecuados de objetivos táctiles garantizan que los usuarios con discapacidades motoras puedan tocar con precisión los elementos interactivos.

Requisitos de Tamaño

WCAG 2.5.5 (Nivel AAA): 44x44 píxeles CSS (aproximadamente 44x44 puntos en iOS, 48x48 dp en Android)

WCAG 2.5.8 (Nivel AA): 24x24 píxeles CSS mínimo

Directrices de Plataforma:

  • Directrices de Interfaz Humana de iOS: mínimo 44x44 puntos
  • Material Design de Android: mínimo 48x48 dp

Ejemplos de Implementación

// iOS - Asegurar objetivo táctil mínimo
class AccessibleButton: UIButton {
    override var intrinsicContentSize: CGSize {
        let size = super.intrinsicContentSize
        return CGSize(
            width: max(size.width, 44),
            height: max(size.height, 44)
        )
    }

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let expandedBounds = bounds.insetBy(dx: -10, dy: -10)
        return expandedBounds.contains(point)
    }
}
// Android - Objetivo táctil mínimo con ViewCompat
fun ensureMinimumTouchTarget(view: View) {
    ViewCompat.setMinimumWidth(view, 48.dp)
    ViewCompat.setMinimumHeight(view, 48.dp)
}

// Agregar delegado táctil para áreas clickeables pequeñas
val parent = smallIcon.parent as View
parent.post {
    val delegateArea = Rect()
    smallIcon.getHitRect(delegateArea)

    // Expandir área táctil 12dp en todas direcciones
    delegateArea.inset(-12.dp, -12.dp)

    parent.touchDelegate = TouchDelegate(delegateArea, smallIcon)
}

Espaciado Entre Objetivos

Mantener espaciado adecuado entre elementos interactivos para prevenir toques accidentales:

<!-- Ejemplo de Layout Android -->
<LinearLayout
    android:orientation="horizontal"
    android:padding="8dp">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="48dp"
        android:minWidth="48dp"
        android:layout_marginEnd="16dp"
        android:text="Cancelar" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="48dp"
        android:minWidth="48dp"
        android:text="Confirmar" />
</LinearLayout>

Pruebas de Navegación con Lector de Pantalla

La navegación efectiva con lector de pantalla garantiza un orden de lectura lógico y flujos de interacción intuitivos.

Probar Orden de Enfoque

// iOS - Orden de enfoque personalizado
override var accessibilityElements: [Any]? {
    get {
        return [
            titleLabel,
            descriptionLabel,
            primaryButton,
            secondaryButton,
            closeButton
        ]
    }
    set {}
}

// Omitir elementos decorativos
decorativeView.isAccessibilityElement = false
// Android - Orden de recorrido de accesibilidad
titleTextView.accessibilityTraversalBefore = descriptionTextView.id
descriptionTextView.accessibilityTraversalBefore = actionButton.id

// Agrupar contenido relacionado
cardView.apply {
    importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
    contentDescription = "Tarjeta de producto: ${product.name}, ${product.price}"
}

Probar Patrones de Navegación

Pruebas de Diálogo Modal:

// iOS - Atrapar enfoque dentro del modal
override func accessibilityPerformEscape() -> Bool {
    dismissModal()
    return true
}

// Asegurar que el enfoque se mueva al modal cuando se muestre
UIAccessibility.post(notification: .screenChanged, argument: modalTitle)

Pruebas de Desplazamiento Infinito:

// Anunciar cuando se carga nuevo contenido
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        if (isLastItemVisible && loadingMore) {
            recyclerView.announceForAccessibility(
                "Cargando más artículos"
            )
        }
    }
})

Tipo Dinámico y Escalado de Fuentes

Soportar tipo dinámico garantiza que el texto permanezca legible para usuarios con discapacidades visuales.

Tipo Dinámico en iOS

// Soportar Dynamic Type
titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
titleLabel.adjustsFontForContentSizeCategory = true

// Fuentes personalizadas con escalado Dynamic Type
let customFont = UIFont(name: "CustomFont", size: 17)!
bodyLabel.font = UIFontMetrics(forTextStyle: .body)
    .scaledFont(for: customFont)
bodyLabel.adjustsFontForContentSizeCategory = true

// Escuchar cambios de tamaño
NotificationCenter.default.addObserver(
    forName: UIContentSizeCategory.didChangeNotification,
    object: nil,
    queue: .main
) { _ in
    self.updateLayout()
}

Texto Escalable en Android

<!-- Usar unidades sp para tamaños de texto -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="16sp"
    android:textAppearance="?attr/textAppearanceBody1" />
// Manejar escalado extremo de fuentes
fun isLargeFontScale(): Boolean {
    val fontScale = resources.configuration.fontScale
    return fontScale >= 1.3f
}

// Ajustar layout para texto grande
if (isLargeFontScale()) {
    linearLayout.orientation = LinearLayout.VERTICAL
} else {
    linearLayout.orientation = LinearLayout.HORIZONTAL
}

Probar Escalado de Fuentes

Pruebas iOS:

  1. Ajustes > Accesibilidad > Pantalla y Tamaño de Texto > Texto Más Grande
  2. Probar todos los tamaños desde XS hasta XXXL
  3. Verificar que el diseño no se rompa en tamaños extremos

Pruebas Android:

  1. Ajustes > Pantalla > Tamaño de fuente
  2. Probar al 100%, 130%, 160%, 200%
  3. Usar adb shell settings put system font_scale 2.0

Herramientas de Escáner de Accesibilidad

Los escáneres automatizados identifican problemas comunes de accesibilidad rápidamente, aunque las pruebas manuales siguen siendo esenciales.

Inspector de Accesibilidad de iOS

Accessibility Inspector de Xcode proporciona auditoría de accesibilidad en tiempo real:

# Lanzar desde Xcode
Xcode > Open Developer Tool > Accessibility Inspector

# Características:
# - Modo de inspección (pasar sobre elementos)
# - Pestaña de auditoría (verificaciones automatizadas)
# - Analizador de contraste de color
# - Visor de jerarquía de elementos

Verificaciones de Auditoría:

  • Etiquetas de accesibilidad faltantes
  • Contraste de color insuficiente
  • Objetivos táctiles pequeños
  • Soporte de Dynamic Type
  • Desajustes de traits

Pruebas por Línea de Comandos:

# Ejecutar auditoría de accesibilidad desde terminal
xcrun simctl spawn booted log stream --predicate \
  'subsystem == "com.apple.accessibility"' --level=debug

Escáner de Accesibilidad de Android

Google Accessibility Scanner es una aplicación independiente que analiza la UI:

# Instalar desde Google Play Store
# O vía ADB:
adb install accessibility-scanner.apk

# Uso:
# 1. Habilitar escáner en ajustes de Accesibilidad
# 2. Tocar botón de acción flotante
# 3. Navegar a pantalla a probar
# 4. Tocar marca de verificación para escanear

El Escáner Identifica:

  • Violaciones de tamaño de objetivo táctil
  • Texto de bajo contraste
  • Descripciones de contenido faltantes
  • Etiquetado de elementos clickeables
  • Problemas de escalado de texto

Herramientas de Pruebas de Accesibilidad Automatizadas

iOS - XCUITest Accessibility:

func testAccessibility() {
    let app = XCUIApplication()
    app.launch()

    // Verificar que los elementos sean accesibles
    let addButton = app.buttons["Agregar al carrito"]
    XCTAssertTrue(addButton.exists)
    XCTAssertTrue(addButton.isHittable)

    // Verificar etiquetas de accesibilidad
    XCTAssertEqual(
        addButton.label,
        "Agregar al carrito"
    )

    // Verificar objetivo táctil mínimo
    let frame = addButton.frame
    XCTAssertGreaterThanOrEqual(frame.width, 44)
    XCTAssertGreaterThanOrEqual(frame.height, 44)
}

Android - Espresso Accessibility:

import androidx.test.espresso.accessibility.AccessibilityChecks

class AccessibilityTest {
    init {
        // Habilitar verificaciones de accesibilidad para todas las pruebas
        AccessibilityChecks.enable()
    }

    @Test
    fun testButtonAccessibility() {
        onView(withId(R.id.add_button))
            .check(matches(isDisplayed()))
            .check(matches(hasContentDescription()))
            .check(matches(hasMinimumTouchTarget(48.dp)))
    }
}

// Matcher personalizado para tamaño de objetivo táctil
fun hasMinimumTouchTarget(minSize: Int) = object : TypeSafeMatcher<View>() {
    override fun describeTo(description: Description) {
        description.appendText("tiene objetivo táctil mínimo de $minSize")
    }

    override fun matchesSafely(view: View): Boolean {
        return view.width >= minSize && view.height >= minSize
    }
}

Pruebas con Usuarios con Discapacidades

La validación verdadera de accesibilidad requiere pruebas con tecnologías de asistencia en escenarios realistas.

Crear Escenarios de Prueba

Escenarios de Discapacidad Visual:

1. Completar registro de usuario usando solo VoiceOver/TalkBack
2. Navegar a detalle de producto y agregar al carrito
3. Completar proceso de pago con lector de pantalla
4. Buscar artículos y filtrar resultados
5. Acceder a funciones de ayuda/soporte
6. Actualizar configuración de cuenta

Escenarios de Discapacidad Motora:

1. Navegar aplicación usando control de interruptor externo
2. Completar formularios usando entrada de voz
3. Interactuar con todos los controles usando objetivos táctiles grandes
4. Probar alternativas de gestos (no se requiere multi-táctil)
5. Verificar extensiones de tiempo de espera para interacciones lentas

Lista de Verificación de Pruebas:

Área de PruebaMétodo de Validación
Lector de PantallaCompletar rutas críticas solo con VoiceOver/TalkBack
Control por VozNavegar e interactuar usando solo comandos de voz
Control por InterruptorOperar aplicación usando dispositivo de interruptor externo
Escalado de FuenteProbar al 200% de escala de fuente, verificar legibilidad
Contraste de ColorValidar con verificador de contraste, probar en escala de grises
Sensibilidad al MovimientoDesactivar animaciones, verificar soporte de movimiento reducido

Herramientas de Tecnología de Asistencia

Tecnologías de Asistencia iOS:

  • VoiceOver (lector de pantalla)
  • Voice Control (navegación por voz)
  • Switch Control (entrada de dispositivo externo)
  • Zoom (magnificación de pantalla)
  • Reduce Motion
  • Increase Contrast

Tecnologías de Asistencia Android:

  • TalkBack (lector de pantalla)
  • Voice Access (navegación por voz)
  • Switch Access (entrada de dispositivo externo)
  • Magnification
  • High contrast text
  • Remove animations

Accesibilidad en Pipelines CI/CD

Integrar pruebas de accesibilidad en CI/CD garantiza que las regresiones se detecten temprano y se mantenga el cumplimiento.

Pruebas de Accesibilidad Automatizadas en CI

iOS - Integración Fastlane:

# Fastfile
lane :accessibility_tests do
  run_tests(
    scheme: "YourApp",
    devices: ["iPhone 14 Pro"],
    only_testing: ["YourAppUITests/AccessibilityTests"]
  )

  # Ejecutar auditoría de accesibilidad personalizada
  sh("ruby scripts/accessibility_audit.rb")
end

# Script de auditoría personalizada
def audit_accessibility
  violations = []

  # Verificar imágenes sin etiquetas de accesibilidad
  violations += find_unlabeled_images

  # Verificar objetivos táctiles pequeños
  violations += find_small_touch_targets

  # Verificar bajo contraste
  violations += find_contrast_violations

  if violations.any?
    fail "Se encontraron #{violations.count} violaciones de accesibilidad"
  end
end

Android - Integración Gradle:

// build.gradle.kts
android {
    testOptions {
        unitTests {
            isIncludeAndroidResources = true
        }
    }
}

dependencies {
    androidTestImplementation("androidx.test.espresso:espresso-accessibility:3.5.1")
    androidTestImplementation("com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:4.0.0")
}

// AccessibilityTest.kt
@RunWith(AndroidJUnit4::class)
class ContinuousAccessibilityTest {
    @get:Rule
    val activityRule = ActivityScenarioRule(MainActivity::class.java)

    @Before
    fun setup() {
        AccessibilityChecks.enable()
            .setRunChecksFromRootView(true)
            .setSuppressingResultMatcher(
                allOf(
                    matchesCheck(DuplicateSpeakableTextCheck::class.java),
                    matchesViews(withId(R.id.allowed_duplicate))
                )
            )
    }

    @Test
    fun testAllScreensAccessibility() {
        // Navegación a través de todas las pantallas principales
        // Las verificaciones de accesibilidad se ejecutan automáticamente
        navigateToHome()
        navigateToProfile()
        navigateToSettings()
    }
}

Verificaciones de Accesibilidad Pre-commit

#!/bin/bash
# .git/hooks/pre-commit

echo "Ejecutando verificaciones de accesibilidad..."

# Verificar etiquetas de accesibilidad faltantes (iOS)
missing_labels=$(grep -r "UIImage\|UIButton" --include="*.swift" | \
  grep -v "accessibilityLabel" | wc -l)

if [ "$missing_labels" -gt 0 ]; then
  echo "Advertencia: Elementos UI potencialmente sin etiquetas de accesibilidad"
fi

# Verificar colores codificados (deberían usar colores semánticos)
hardcoded_colors=$(grep -r "UIColor(red:" --include="*.swift" | wc -l)

if [ "$hardcoded_colors" -gt 5 ]; then
  echo "Advertencia: Colores codificados encontrados. Considere usar colores semánticos"
fi

# Ejecutar pruebas unitarias de accesibilidad
npm run test:accessibility

if [ $? -ne 0 ]; then
  echo "Las pruebas de accesibilidad fallaron. Por favor corrija antes de hacer commit."
  exit 1
fi

echo "Verificaciones de accesibilidad pasaron!"

Panel de Informes de Accesibilidad

// accessibility-report.js
const fs = require('fs');
const { execSync } = require('child_process');

function generateAccessibilityReport() {
  const report = {
    timestamp: new Date().toISOString(),
    platform: process.env.PLATFORM,
    violations: [],
    warnings: [],
    passed: []
  };

  // Ejecutar pruebas automatizadas
  const testResults = JSON.parse(
    execSync('npm run test:accessibility:json').toString()
  );

  // Categorizar resultados
  testResults.forEach(result => {
    if (result.severity === 'error') {
      report.violations.push(result);
    } else if (result.severity === 'warning') {
      report.warnings.push(result);
    } else {
      report.passed.push(result);
    }
  });

  // Calcular puntuación de cumplimiento
  const total = report.violations.length +
                report.warnings.length +
                report.passed.length;
  report.complianceScore =
    ((report.passed.length / total) * 100).toFixed(2);

  // Generar informe HTML
  const html = generateHTMLReport(report);
  fs.writeFileSync('accessibility-report.html', html);

  // Fallar build si se encuentran violaciones críticas
  if (report.violations.length > 0) {
    console.error(`Se encontraron ${report.violations.length} violaciones de accesibilidad`);
    process.exit(1);
  }

  console.log(`Cumplimiento de accesibilidad: ${report.complianceScore}%`);
}

generateAccessibilityReport();

Conclusión

Las pruebas de accesibilidad móvil son un proceso continuo que requiere tanto herramientas automatizadas como validación manual con tecnologías de asistencia. Al implementar estrategias de pruebas de accesibilidad integrales—desde pruebas con VoiceOver y TalkBack hasta integración automatizada en CI/CD—garantiza que sus aplicaciones móviles sean utilizables por todos.

Conclusiones clave:

  • Comience temprano: Integre la accesibilidad desde la fase de diseño
  • Pruebe con usuarios reales: Las herramientas automatizadas detectan solo el 30-40% de los problemas
  • Use herramientas de plataforma: VoiceOver, TalkBack, Accessibility Inspector
  • Automatice pruebas de regresión: Prevenga que los errores de accesibilidad reaparezcan
  • Eduque a su equipo: La accesibilidad es responsabilidad de todos

El cumplimiento de WCAG no se trata solo de evitar problemas legales—se trata de crear mejores productos que sirvan a todos los usuarios. Cada mejora de accesibilidad beneficia a todos, desde usuarios con discapacidades permanentes hasta aquellos que experimentan impedimentos temporales o limitaciones situacionales.

Invierta en pruebas de accesibilidad y construirá aplicaciones móviles más robustas, inclusivas y exitosas.