Введение в Тестирование Мобильной Доступности

Тестирование мобильной доступности гарантирует, что приложения могут использовать все, включая людей с нарушениями зрения, слуха, двигательных функций или когнитивными нарушениями. Более миллиарда людей во всем мире живут с какой-либо формой инвалидности, поэтому доступность — это не просто юридическое требование, а фундаментальный аспект инклюзивного дизайна, который расширяет вашу пользовательскую базу и улучшает опыт всех пользователей.

Тестирование мобильной доступности выходит за рамки базового функционального тестирования. Оно требует понимания того, как вспомогательные технологии взаимодействуют с вашим приложением, обеспечения соответствия Руководству по доступности веб-контента (WCAG) и проверки того, что пользователи с ограниченными возможностями могут самостоятельно выполнять все критически важные пользовательские сценарии.

Ставки высоки: недоступные приложения могут столкнуться с судебными исками в соответствии с Законом об американцах с ограниченными возможностями (ADA), Разделом 508 и аналогичными нормативными актами по всему миру. Что еще важнее, недоступные приложения исключают миллионы потенциальных пользователей и наносят ущерб репутации бренда.

Руководства WCAG 2.1 и 2.2 для Мобильных Устройств

Руководство по доступности веб-контента (WCAG) 2.1 представило критерии успеха, специфичные для мобильных устройств, а WCAG 2.2 добавил дополнительные уточнения. Эти руководства организованы вокруг четырех принципов: Воспринимаемость, Управляемость, Понятность и Надежность (POUR).

Ключевые Критерии Успеха WCAG для Мобильных Устройств

КритерийУровеньФокус на Мобильные
1.3.4 ОриентацияAAПоддержка портретного и ландшафтного режимов
1.3.5 Определение Назначения ВводаAAПрограммное определение назначения полей ввода
1.4.10 ПерекомпоновкаAAАдаптация контента без горизонтальной прокрутки
1.4.11 Контраст Нетекстовых ЭлементовAAКонтраст 3:1 для компонентов UI и графики
1.4.12 Интервал ТекстаAAПоддержка пользовательских настроек интервалов текста
1.4.13 Контент при Наведении/ФокусеAAОтклоняемый, наводимый и устойчивый контент
2.5.1 Жесты УказателяAАльтернативы для многоточечных или траекторных жестов
2.5.2 Отмена УказателяAВозможность прерывания или отмены сенсорных действий
2.5.3 Метка в ИмениAВизуальные метки соответствуют программным именам
2.5.4 Активация ДвижениемAАльтернативы для ввода, основанного на движении
2.5.5 Размер ЦелиAAAМинимум 44x44 CSS пикселей для сенсорных целей
2.5.7 Движения ПеретаскиванияAAАльтернативы с одним указателем для перетаскивания
2.5.8 Размер Цели (Минимальный)AAМинимум 24x24 CSS пикселей для целей

Уровни Соответствия

  • Уровень A: Основные функции доступности (минимальное требование)
  • Уровень AA: Отраслевой стандарт и юридическое требование для большинства юрисдикций
  • Уровень AAA: Расширенная доступность (золотой стандарт)

Большинство организаций нацелены на соответствие WCAG 2.1 Уровня AA, который охватывает большинство потребностей в доступности без чрезмерной сложности достижения.

Тестирование с VoiceOver на iOS

VoiceOver — это встроенная программа чтения с экрана Apple для iOS и iPadOS. Тестирование с VoiceOver гарантирует, что пользователи с нарушениями зрения могут эффективно навигировать и взаимодействовать с вашим приложением.

Включение VoiceOver

Быстрое Включение: Тройной щелчок боковой кнопки (настроить в Настройки > Универсальный доступ > Быстрая команда универсального доступа)

Ручное Включение: Настройки > Универсальный доступ > VoiceOver > Включить

Основные Жесты VoiceOver

Одиночное Касание          - Выбрать и озвучить элемент
Двойное Касание            - Активировать выбранный элемент
Смахивание Вправо          - Следующий элемент
Смахивание Влево           - Предыдущий элемент
Касание Двумя Пальцами     - Пауза/возобновление VoiceOver
Смахивание Вверх (3 пальца) - Прокрутка вниз
Смахивание Вниз (3 пальца)  - Прокрутка вверх
Жест Ротора                - Два пальца вращаются на экране

Контрольный Список Тестирования VoiceOver

1. Тестирование Потока Навигации

// Пример правильной метки доступности
button.accessibilityLabel = "Добавить в корзину"
button.accessibilityHint = "Добавляет товар в корзину покупок"
button.accessibilityTraits = .button

// Плохой пример - отсутствие контекста
button.accessibilityLabel = "Добавить" // Слишком расплывчато

2. Тестирование Навигации с Ротором

Ротор VoiceOver позволяет пользователям навигировать по заголовкам, ссылкам, элементам форм и др. Проверьте что:

  • Заголовки правильно помечены с accessibilityTraits = .header
  • Поля форм логически сгруппированы
  • Пользовательские действия ротора работают как ожидается
// Создание пользовательского ротора для разделов таблицы
let customRotor = UIAccessibilityCustomRotor(name: "Разделы") { predicate in
    // Пользовательская логика навигации
    return UIAccessibilityCustomRotorItemResult(
        targetElement: nextSection,
        targetRange: nil
    )
}
accessibilityCustomRotors = [customRotor]

3. Тестирование Динамического Контента

Когда контент обновляется, пользователям VoiceOver нужны уведомления:

// Объявить изменения пользователям программы чтения с экрана
UIAccessibility.post(
    notification: .announcement,
    argument: "Корзина обновлена, содержит 3 товара"
)

// Уведомить об изменениях макета
UIAccessibility.post(
    notification: .layoutChanged,
    argument: firstNewElement
)

4. Альтернативный Текст для Изображений

// Декоративные изображения
decorativeImage.isAccessibilityElement = false

// Информативные изображения
productImage.accessibilityLabel = "Синие кроссовки, размер 10"

// Сложные изображения (графики, диаграммы)
chartView.accessibilityLabel = "График доходов"
chartView.accessibilityValue = "Q1: $50K, Q2: $75K, Q3: $100K, Q4: $125K"

Тестирование с TalkBack на Android

TalkBack — это программа чтения с экрана Google для устройств Android. Она обеспечивает голосовую обратную связь и позволяет пользователям навигировать по приложениям, используя жесты и внешние клавиатуры.

Включение TalkBack

Быстрое Включение: Ярлык клавиш громкости (настроить в Настройки > Специальные возможности > TalkBack > Ярлык TalkBack)

Ручное Включение: Настройки > Специальные возможности > TalkBack > Включить

Основные Жесты TalkBack

Одиночное Касание           - Исследование касанием
Двойное Касание             - Активировать выбранный элемент
Смахивание Вправо           - Следующий элемент
Смахивание Влево            - Предыдущий элемент
Смахивание Вниз-Вправо      - Чтение сверху
Смахивание Вверх-Вправо     - Чтение с текущей позиции
Смахивание Влево-Вправо     - Навигация назад
Смахивание Вправо-Влево     - Домой

Реализация Тестирования TalkBack

1. Описания Контента

// Кнопка с четким описанием
addButton.contentDescription = "Добавить товар в корзину"

// ImageView с осмысленным описанием
productImage.contentDescription = "Красный кожаный кошелек с золотой молнией"

// Декоративные изображения - скрыть от TalkBack
decorativeIcon.importantForAccessibility =
    View.IMPORTANT_FOR_ACCESSIBILITY_NO

// Группировка связанных элементов
containerLayout.contentDescription = "Карточка профиля пользователя"
containerLayout.importantForAccessibility =
    View.IMPORTANT_FOR_ACCESSIBILITY_YES

2. Пользовательские Действия

Разрешить пользователям выполнять несколько действий над одним элементом:

ViewCompat.addAccessibilityAction(
    emailItem,
    "Удалить письмо"
) { view, arguments ->
    deleteEmail(view)
    true
}

ViewCompat.addAccessibilityAction(
    emailItem,
    "Пометить как прочитанное"
) { view, arguments ->
    markAsRead(view)
    true
}

3. Объявления Активной Области

// Объявлять изменения динамического контента
statusTextView.accessibilityLiveRegion =
    View.ACCESSIBILITY_LIVE_REGION_POLITE

// Для срочных объявлений
errorTextView.accessibilityLiveRegion =
    View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE

// Программное объявление
view.announceForAccessibility("Загрузка завершена")

4. Навигация по Заголовкам

// Пометить представление как заголовок для навигации
titleTextView.accessibilityHeading = true

// Или используя ViewCompat
ViewCompat.setAccessibilityHeading(titleTextView, true)

Метки и Подсказки Доступности

Эффективные метки и подсказки доступности имеют решающее значение для того, чтобы пользователи программ чтения с экрана понимали элементы интерфейса.

Лучшие Практики Метка vs. Подсказка

Метки Доступности: Что представляет собой элемент Подсказки Доступности: Что делает элемент при активации

// Пример iOS
saveButton.accessibilityLabel = "Сохранить документ"
saveButton.accessibilityHint = "Сохраняет ваш документ в облачное хранилище"

// Пример Android
saveButton (как обсуждается в [Cross-Platform Mobile Testing: Strategies for Multi-Device Success](/blog/cross-platform-mobile-testing)) (как обсуждается в [Appium 2.0: New Architecture and Cloud Integration for Modern Mobile Testing](/blog/appium-2-architecture-cloud)).contentDescription = "Сохранить документ"
saveButton.tooltipText = "Сохраняет ваш документ в облачное хранилище"

Рекомендации по Меткам

Хорошая ПрактикаПлохая Практика
“Поиск товаров”“Поиск” (отсутствие контекста)
“Фото профиля”“Изображение” (обобщенно)
“Рейтинг 5 звезд”“Звезды” (отсутствует значение)
“Закрыть диалог”“X” (неясное действие)
“Громкость: 75%”“Ползунок” (отсутствует состояние)

Контекстно-Зависимые Метки

// Динамические метки на основе состояния
fun updatePlayButtonLabel() {
    playButton.contentDescription = when (mediaPlayer.isPlaying) {
        true -> "Приостановить аудио"
        false -> "Воспроизвести аудио"
    }
}

// Корзина с количеством товаров
fun updateCartLabel(itemCount: Int) {
    cartButton.contentDescription = when (itemCount) {
        0 -> "Корзина покупок, пуста"
        1 -> "Корзина покупок, 1 товар"
        else -> "Корзина покупок, $itemCount товаров"
    }
}

Контраст Цвета и Визуальный Дизайн

WCAG требует достаточного цветового контраста, чтобы гарантировать, что текст и элементы пользовательского интерфейса воспринимаются пользователями с нарушениями зрения.

Требования к Контрасту

Тип КонтентаWCAG Уровень AAWCAG Уровень AAA
Обычный текст (< 18pt)4.5:17:1
Крупный текст (≥ 18pt или ≥ 14pt жирный)3:14.5:1
Компоненты UI и графика3:1Н/Д
Отключенные/неактивные элементыНет требованийНет требований

Тестирование Контраста Цвета

// iOS - Поддержка Dynamic Type с правильным контрастом
extension UIColor {
    static func accessibleText(on background: UIColor) -> UIColor {
        // Рассчитать яркость и вернуть подходящий цвет текста
        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)
    }
}

Независимость от Цвета

Никогда не полагайтесь исключительно на цвет для передачи информации:

// Плохо: Только цвет указывает состояние
statusIndicator.setBackgroundColor(Color.RED) // Состояние ошибки

// Хорошо: Цвет + иконка + текст
statusIndicator.apply {
    setBackgroundColor(Color.RED)
    setImageResource(R.drawable.ic_error)
    contentDescription = "Ошибка: Соединение не удалось"
}

// Хорошо: Паттерн + цвет для графиков
chartView.apply {
    // Использовать разные паттерны/текстуры для серий данных
    series1.pattern = Pattern.SOLID
    series2.pattern = Pattern.STRIPED
    series3.pattern = Pattern.DOTTED
}

Размер и Интервал Сенсорных Целей

Адекватные размеры сенсорных целей гарантируют, что пользователи с двигательными нарушениями могут точно нажимать на интерактивные элементы.

Требования к Размеру

WCAG 2.5.5 (Уровень AAA): 44x44 CSS пикселей (примерно 44x44 точек на iOS, 48x48 dp на Android)

WCAG (как обсуждается в Detox: Grey-Box Testing for React Native Applications) 2.5.8 (Уровень AA): минимум 24x24 CSS пикселей

Рекомендации Платформы:

  • Руководство по интерфейсу iOS: минимум 44x44 точек
  • Material Design Android: минимум 48x48 dp

Примеры Реализации

// iOS - Обеспечение минимальной сенсорной цели
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 - Минимальная сенсорная цель с ViewCompat
fun ensureMinimumTouchTarget(view: View) {
    ViewCompat.setMinimumWidth(view, 48.dp)
    ViewCompat.setMinimumHeight(view, 48.dp)
}

// Добавить делегата касания для маленьких кликабельных областей
val parent = smallIcon.parent as View
parent.post {
    val delegateArea = Rect()
    smallIcon.getHitRect(delegateArea)

    // Расширить область касания на 12dp во всех направлениях
    delegateArea.inset(-12.dp, -12.dp)

    parent.touchDelegate = TouchDelegate(delegateArea, smallIcon)
}

Интервал Между Целями

Поддерживайте адекватный интервал между интерактивными элементами для предотвращения случайных нажатий:

<!-- Пример Разметки 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="Отменить" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="48dp"
        android:minWidth="48dp"
        android:text="Подтвердить" />
</LinearLayout>

Тестирование Навигации с Программой Чтения с Экрана

Эффективная навигация с программой чтения с экрана обеспечивает логический порядок чтения и интуитивно понятные потоки взаимодействия.

Тестирование Порядка Фокуса

// iOS - Пользовательский порядок фокуса
override var accessibilityElements: [Any]? {
    get {
        return [
            titleLabel,
            descriptionLabel,
            primaryButton,
            secondaryButton,
            closeButton
        ]
    }
    set {}
}

// Пропустить декоративные элементы
decorativeView.isAccessibilityElement = false
// Android - Порядок обхода доступности
titleTextView.accessibilityTraversalBefore = descriptionTextView.id
descriptionTextView.accessibilityTraversalBefore = actionButton.id

// Группировка связанного контента
cardView.apply {
    importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
    contentDescription = "Карточка товара: ${product.name}, ${product.price}"
}

Тестирование Паттернов Навигации

Тестирование Модального Диалога:

// iOS - Захват фокуса в модальном окне
override func accessibilityPerformEscape() -> Bool {
    dismissModal()
    return true
}

// Обеспечить перемещение фокуса в модальное окно при показе
UIAccessibility.post(notification: .screenChanged, argument: modalTitle)

Тестирование Бесконечной Прокрутки:

// Объявлять при загрузке нового контента
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        if (isLastItemVisible && loadingMore) {
            recyclerView.announceForAccessibility(
                "Загрузка дополнительных элементов"
            )
        }
    }
})

Динамический Тип и Масштабирование Шрифтов

Поддержка динамического типа гарантирует, что текст остается читаемым для пользователей с нарушениями зрения.

Динамический Тип в iOS

// Поддержка Dynamic Type
titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
titleLabel.adjustsFontForContentSizeCategory = true

// Пользовательские шрифты с масштабированием Dynamic Type
let customFont = UIFont(name: "CustomFont", size: 17)!
bodyLabel.font = UIFontMetrics(forTextStyle: .body)
    .scaledFont(for: customFont)
bodyLabel.adjustsFontForContentSizeCategory = true

// Прослушивание изменений размера
NotificationCenter.default.addObserver(
    forName: UIContentSizeCategory.didChangeNotification,
    object: nil,
    queue: .main
) { _ in
    self.updateLayout()
}

Масштабируемый Текст в Android

<!-- Использовать единицы sp для размеров текста -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="16sp"
    android:textAppearance="?attr/textAppearanceBody1" />
// Обработка экстремального масштабирования шрифтов
fun isLargeFontScale(): Boolean {
    val fontScale = resources.configuration.fontScale
    return fontScale >= 1.3f
}

// Настройка макета для крупного текста
if (isLargeFontScale()) {
    linearLayout.orientation = LinearLayout.VERTICAL
} else {
    linearLayout.orientation = LinearLayout.HORIZONTAL
}

Тестирование Масштабирования Шрифтов

Тестирование iOS:

  1. Настройки > Универсальный доступ > Экран и размер текста > Увеличенный текст
  2. Проверить все размеры от XS до XXXL
  3. Убедиться, что макет не ломается при экстремальных размерах

Тестирование Android:

  1. Настройки > Экран > Размер шрифта
  2. Проверить при 100%, 130%, 160%, 200%
  3. Использовать adb shell settings put system font_scale 2.0

Инструменты Сканера Доступности

Автоматизированные сканеры быстро выявляют распространенные проблемы доступности, хотя ручное тестирование остается необходимым.

Инспектор Доступности iOS

Accessibility Inspector Xcode обеспечивает аудит доступности в реальном времени:

# Запуск из Xcode
Xcode > Open Developer Tool > Accessibility Inspector

# Функции:
# - Режим инспекции (наведение на элементы)
# - Вкладка аудита (автоматизированные проверки)
# - Анализатор контраста цвета
# - Просмотрщик иерархии элементов

Проверки Аудита:

  • Отсутствующие метки доступности
  • Недостаточный контраст цвета
  • Маленькие сенсорные цели
  • Поддержка Dynamic Type
  • Несоответствия трейтов

Тестирование из Командной Строки:

# Запустить аудит доступности из терминала
xcrun simctl spawn booted log stream --predicate \
  'subsystem == "com.apple.accessibility"' --level=debug

Сканер Доступности Android

Google Accessibility Scanner — это отдельное приложение, анализирующее UI:

# Установить из Google Play Store
# Или через ADB:
adb install accessibility-scanner.apk

# Использование:
# 1. Включить сканер в настройках Специальных возможностей
# 2. Нажать на плавающую кнопку действия
# 3. Перейти на экран для тестирования
# 4. Нажать на галочку для сканирования

Сканер Выявляет:

  • Нарушения размера сенсорной цели
  • Текст с низким контрастом
  • Отсутствующие описания контента
  • Маркировка кликабельных элементов
  • Проблемы масштабирования текста

Инструменты Автоматизированного Тестирования Доступности

iOS - XCUITest Accessibility:

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

    // Проверить доступность элементов
    let addButton = app.buttons["Добавить в корзину"]
    XCTAssertTrue(addButton.exists)
    XCTAssertTrue(addButton.isHittable)

    // Проверить метки доступности
    XCTAssertEqual(
        addButton.label,
        "Добавить в корзину"
    )

    // Проверить минимальную сенсорную цель
    let frame = addButton.frame
    XCTAssertGreaterThanOrEqual(frame.width, 44)
    XCTAssertGreaterThanOrEqual(frame.height, 44)
}

Android - Espresso Accessibility:

import androidx.test.espresso.accessibility.AccessibilityChecks

class AccessibilityTest {
    init {
        // Включить проверки доступности для всех тестов
        AccessibilityChecks.enable()
    }

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

// Пользовательский matcher для размера сенсорной цели
fun hasMinimumTouchTarget(minSize: Int) = object : TypeSafeMatcher<View>() {
    override fun describeTo(description: Description) {
        description.appendText("имеет минимальную сенсорную цель $minSize")
    }

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

Тестирование для Пользователей с Ограниченными Возможностями

Настоящая проверка доступности требует тестирования со вспомогательными технологиями в реалистичных сценариях.

Создание Тестовых Сценариев

Сценарии Нарушения Зрения:

1. Завершить регистрацию пользователя только с VoiceOver/TalkBack
2. Перейти к деталям товара и добавить в корзину
3. Завершить процесс оформления заказа с программой чтения с экрана
4. Искать товары и фильтровать результаты
5. Получить доступ к функциям помощи/поддержки
6. Обновить настройки учетной записи

Сценарии Двигательных Нарушений:

1. Навигация по приложению с использованием внешнего переключателя
2. Заполнение форм с использованием голосового ввода
3. Взаимодействие со всеми элементами управления с большими сенсорными целями
4. Тестирование альтернатив жестов (не требуется мультитач)
5. Проверка продления таймаутов для медленных взаимодействий

Контрольный Список Тестирования:

Область ТестированияМетод Проверки
Программа Чтения с ЭкранаВыполнить критические пути только с VoiceOver/TalkBack
Голосовое УправлениеНавигация и взаимодействие только голосовыми командами
Управление ПереключателемУправление приложением с помощью внешнего переключателя
Масштабирование ШрифтаТестирование при 200% масштабе шрифта, проверка читаемости
Контраст ЦветаПроверка с помощью анализатора контраста, тестирование в оттенках серого
Чувствительность к ДвижениюОтключение анимации, проверка поддержки уменьшенного движения

Инструменты Вспомогательных Технологий

Вспомогательные Технологии iOS:

  • VoiceOver (программа чтения с экрана)
  • Voice Control (голосовая навигация)
  • Switch Control (ввод с внешнего устройства)
  • Zoom (увеличение экрана)
  • Reduce Motion
  • Increase Contrast

Вспомогательные Технологии Android:

  • TalkBack (программа чтения с экрана)
  • Voice Access (голосовая навигация)
  • Switch Access (ввод с внешнего устройства)
  • Magnification
  • High contrast text
  • Remove animations

Доступность в Конвейерах CI/CD

Интеграция тестирования доступности в CI/CD гарантирует, что регрессии выявляются на ранней стадии и поддерживается соответствие.

Автоматизированное Тестирование Доступности в CI

iOS - Интеграция Fastlane:

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

  # Запустить пользовательский аудит доступности
  sh("ruby scripts/accessibility_audit.rb")
end

# Пользовательский скрипт аудита
def audit_accessibility
  violations = []

  # Проверить изображения без меток доступности
  violations += find_unlabeled_images

  # Проверить маленькие сенсорные цели
  violations += find_small_touch_targets

  # Проверить низкий контраст
  violations += find_contrast_violations

  if violations.any?
    fail "Найдено #{violations.count} нарушений доступности"
  end
end

Android - Интеграция 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() {
        // Навигация по всем основным экранам
        // Проверки доступности выполняются автоматически
        navigateToHome()
        navigateToProfile()
        navigateToSettings()
    }
}

Проверки Доступности Pre-commit

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

echo "Запуск проверок доступности..."

# Проверка отсутствующих меток доступности (iOS)
missing_labels=$(grep -r "UIImage\|UIButton" --include="*.swift" | \
  grep -v "accessibilityLabel" | wc -l)

if [ "$missing_labels" -gt 0 ]; then
  echo "Предупреждение: Элементы UI потенциально без меток доступности"
fi

# Проверка жестко закодированных цветов (должны использовать семантические цвета)
hardcoded_colors=$(grep -r "UIColor(red:" --include="*.swift" | wc -l)

if [ "$hardcoded_colors" -gt 5 ]; then
  echo "Предупреждение: Найдены жестко закодированные цвета. Рассмотрите использование семантических цветов"
fi

# Запуск модульных тестов доступности
npm run test:accessibility

if [ $? -ne 0 ]; then
  echo "Тесты доступности не прошли. Пожалуйста, исправьте перед коммитом."
  exit 1
fi

echo "Проверки доступности пройдены!"

Панель Отчетов о Доступности

// 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: []
  };

  // Запустить автоматизированные тесты
  const testResults = JSON.parse(
    execSync('npm run test:accessibility:json').toString()
  );

  // Категоризировать результаты
  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);
    }
  });

  // Рассчитать оценку соответствия
  const total = report.violations.length +
                report.warnings.length +
                report.passed.length;
  report.complianceScore =
    ((report.passed.length / total) * 100).toFixed(2);

  // Сгенерировать HTML отчет
  const html = generateHTMLReport(report);
  fs.writeFileSync('accessibility-report.html', html);

  // Провалить сборку при обнаружении критических нарушений
  if (report.violations.length > 0) {
    console.error(`Найдено ${report.violations.length} нарушений доступности`);
    process.exit(1);
  }

  console.log(`Соответствие доступности: ${report.complianceScore}%`);
}

generateAccessibilityReport();

Заключение

Тестирование мобильной доступности — это непрерывный процесс, требующий как автоматизированных инструментов, так и ручной проверки со вспомогательными технологиями. Внедряя комплексные стратегии тестирования доступности—от тестирования с VoiceOver и TalkBack до автоматизированной интеграции в CI/CD—вы гарантируете, что ваши мобильные приложения могут использовать все.

Ключевые выводы:

  • Начинайте рано: Интегрируйте доступность с этапа проектирования
  • Тестируйте с реальными пользователями: Автоматизированные инструменты выявляют только 30-40% проблем
  • Используйте инструменты платформы: VoiceOver, TalkBack, Accessibility Inspector
  • Автоматизируйте регрессионное тестирование: Предотвращайте повторное появление ошибок доступности
  • Обучайте свою команду: Доступность — это ответственность каждого

Соответствие WCAG — это не только избежание юридических проблем—это создание лучших продуктов, которые обслуживают всех пользователей. Каждое улучшение доступности приносит пользу всем, от пользователей с постоянными нарушениями до тех, кто испытывает временные нарушения или ситуационные ограничения.

Инвестируйте в тестирование доступности, и вы создадите более надежные, инклюзивные и успешные мобильные приложения.