Тестирование доступности мобильных приложений больше не является опциональным — это юридически и этически необходимо. По данным Всемирной организации здравоохранения, более 1,3 миллиарда человек во всём мире живут с какой-либо формой инвалидности, что составляет 16% населения планеты. Только в США Закон об Americans with Disabilities Act (ADA) ежегодно приводит к более чем 4000 судебных исков в сфере цифровой доступности. Исследование WebAIM 2023 года показало, что более 96% протестированных мобильных главных экранов имели обнаруживаемые сбои доступности. Это руководство охватывает комплексные стратегии тестирования доступности мобильных приложений, включая автоматизированные инструменты и проверку соответствия WCAG 2.2.

TL;DR: Тестирование доступности мобильных приложений требует сочетания автоматического сканирования (axe-core, Accessibility Scanner), ручного тестирования экранными дикторами (VoiceOver на iOS, TalkBack на Android) и проверки соответствия WCAG 2.2. Автоматизированные инструменты выявляют лишь около 30% проблем.

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

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

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

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

Для полного понимания экосистемы мобильного тестирования ознакомьтесь с нашим руководством Mobile Testing 2025: iOS, Android and Beyond и стратегиями кросс-платформенного тестирования. Тестирование доступности интегрируется с фреймворками автоматизации, такими как Appium 2.0, и нативными инструментами, такими как XCTest для iOS.

“Mobile accessibility is not a feature you add at the end — it’s a quality attribute you test throughout. Every sprint without accessibility testing creates debt that’s exponentially harder to fix later.” — Yuri Kan, Senior QA Lead

Руководства 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](/ru/blog/cross-platform-mobile-testing)) (как обсуждается в [Appium 2.0: New Architecture and Cloud Integration for Modern Mobile Testing](/ru/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 — это не только избежание юридических проблем—это создание лучших продуктов, которые обслуживают всех пользователей. Каждое улучшение доступности приносит пользу всем, от пользователей с постоянными нарушениями до тех, кто испытывает временные нарушения или ситуационные ограничения.

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

Смотрите также

Официальные ресурсы

FAQ

Что такое WCAG и почему это применимо к мобильным приложениям?

WCAG (Web Content Accessibility Guidelines) определяет стандарты доступности, используемые во всём мире. Мобильные приложения должны соответствовать минимум WCAG 2.1 AA, охватывая воспринимаемость, управляемость, понятность и надёжность (принципы POUR).

Как тестировать с экранными дикторами?

На iOS: включи VoiceOver в Настройки > Доступность, затем используй жесты смахивания. На Android: включи TalkBack в Настройки > Доступность. Тестируй все интерактивные элементы на правильные объявления меток, порядок фокуса и описания действий.

Какие автоматизированные инструменты существуют?

Google Accessibility Scanner (Android), Apple Accessibility Inspector (iOS), axe-core для гибридных приложений и Appium с плагином axe-mobile. Они автоматически выявляют проблемы с контрастом, отсутствующими метками и размерами зон касания.

Какие размеры зон касания требуются для доступности?

WCAG 2.5.5 рекомендует минимум 44x44 CSS пикселя. Руководство Apple HIG указывает минимум 44x44 точки. Material Design Google рекомендует 48x48 dp. Тестируй все кнопки и интерактивные элементы по этим минимумам.

See Also