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
Criterio | Nivel | Enfoque Móvil |
---|---|---|
1.3.4 Orientación | AA | Soportar modos vertical y horizontal |
1.3.5 Identificar Propósito de Entrada | AA | Identificar programáticamente propósitos de campos |
1.4.10 Reflujo | AA | Contenido adaptable sin desplazamiento horizontal |
1.4.11 Contraste No Textual | AA | Contraste 3:1 para componentes UI y gráficos |
1.4.12 Espaciado de Texto | AA | Soportar preferencias de espaciado de usuario |
1.4.13 Contenido al Pasar/Enfocar | AA | Contenido descartable, sobre el que se puede pasar y persistente |
2.5.1 Gestos de Puntero | A | Alternativas para gestos multipunto o basados en trayectoria |
2.5.2 Cancelación de Puntero | A | Capacidad de abortar o deshacer acciones táctiles |
2.5.3 Etiqueta en el Nombre | A | Etiquetas visuales coinciden con nombres programáticos |
2.5.4 Activación por Movimiento | A | Alternativas para entrada basada en movimiento |
2.5.5 Tamaño del Objetivo | AAA | Mínimo 44x44 píxeles CSS para objetivos táctiles |
2.5.7 Movimientos de Arrastre | AA | Alternativas de puntero único para arrastrar |
2.5.8 Tamaño del Objetivo (Mínimo) | AA | Al 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áctica | Mala 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 Contenido | WCAG Nivel AA | WCAG Nivel AAA |
---|---|---|
Texto normal (< 18pt) | 4.5:1 | 7:1 |
Texto grande (≥ 18pt o ≥ 14pt negrita) | 3:1 | 4.5:1 |
Componentes UI y gráficos | 3:1 | N/A |
Elementos deshabilitados/inactivos | Sin requisito | Sin 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:
- Ajustes > Accesibilidad > Pantalla y Tamaño de Texto > Texto Más Grande
- Probar todos los tamaños desde XS hasta XXXL
- Verificar que el diseño no se rompa en tamaños extremos
Pruebas Android:
- Ajustes > Pantalla > Tamaño de fuente
- Probar al 100%, 130%, 160%, 200%
- 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 Prueba | Método de Validación |
---|---|
Lector de Pantalla | Completar rutas críticas solo con VoiceOver/TalkBack |
Control por Voz | Navegar e interactuar usando solo comandos de voz |
Control por Interruptor | Operar aplicación usando dispositivo de interruptor externo |
Escalado de Fuente | Probar al 200% de escala de fuente, verificar legibilidad |
Contraste de Color | Validar con verificador de contraste, probar en escala de grises |
Sensibilidad al Movimiento | Desactivar 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.