В нашей практике VoiceOver на iPhone читает кнопку «Button» вместо «Добавить в корзину» — потому что у UIImageView с иконкой корзины не задан accessibilityLabel. Пользователь с нарушениями зрения нажимает вслепую. Это не теоретическая ситуация: 2.2 миллиарда человек имеют нарушения зрения, и Apple/Google начали отклонять приложения с грубыми Accessibility-нарушениями при ревью. Мы — команда инженеров с 6-летним опытом в мобильной разработке; на счету более 40 аудитов доступности. Мы проводим аудит доступности и гарантируем соответствие стандартам WCAG 2.1 AA.
Почему accessibilityLabel так часто отсутствует?
Кастомные компоненты — слайдеры, кастомные кнопки, иконки без текста — не имеют автоматических меток. VoiceOver читает координаты или имя класса. На iOS нужно явно ставить accessibilityLabel и accessibilityHint. На Android — contentDescription в XML или через ViewCompat.setAccessibilityDelegate. Разработчики часто забывают задать метку при создании кастомного view. В 70% случаев проблема решается добавлением одной строки кода.
Как правильно организовать фокус после закрытия модалки?
После закрытия модалки фокус VoiceOver/TalkBack остаётся на элементах, которых больше нет на экране — пользователь теряется. На iOS управляем через UIAccessibility.post(notification: .screenChanged, argument: targetView). На Android — ViewCompat.setAccessibilityPaneTitle и sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED). Ручная проверка этого сценария обязательна: автоматические инструменты не всегда ловят потерю фокуса.
Инструменты тестирования
Accessibility Inspector (Xcode). Запускается через Xcode → Open Developer Tool → Accessibility Inspector. Аудит одним кликом — находит отсутствующие метки, мелкие touch targets, проблемы с контрастом. Работает на симуляторе и на реальном устройстве.
Accessibility Scanner (Android). Приложение от Google, сканирует экран и выдаёт список проблем с классификацией по типу. Интегрируется в Espresso через AccessibilityChecks.enable() — автоматически запускает проверки при каждом действии в тесте:
@Before
fun setup() {
AccessibilityChecks.enable()
.setRunChecksFromRootView(true)
}
Каждый Espresso-тест теперь дополнительно проверяет a11y — любое нарушение роняет тест. Accessibility Scanner обрабатывает экран за 2–3 секунды, что в 5 раз быстрее ручной проверки.
Ручное тестирование с VoiceOver/TalkBack. Проходим ключевые сценарии: регистрация, покупка, основной user flow — только через жесты скринридера, без визуального контроля. Это выявляет семантические проблемы, которые автоматика не поймёт: логический порядок чтения элементов, непонятные формулировки меток, потерянный фокус.
axe DevTools Mobile. Коммерческий инструмент с более детальной классификацией нарушений по WCAG. Выдаёт отчёт с severity и ссылками на критерии. Полезен при подготовке к EU Accessibility Act compliance.
Когда требуется ручное тестирование?
Автоматические инструменты не проверяют логику чтения и удобство восприятия. Если приложение содержит сложные анимации, динамические списки или нестандартные жесты — без ручного прогона с VoiceOver/TalkBack не обойтись. Мы рекомендуем проводить ручное тестирование после каждого значительного релиза.
Матрица проверок
| Нарушение |
Автотест |
Ручной тест |
| Отсутствие accessibilityLabel |
Accessibility Scanner / Inspector |
VoiceOver |
| Неверный порядок фокуса |
— |
VoiceOver / TalkBack |
| Touch target < 44pt/48dp |
Accessibility Inspector |
— |
| Контрастность |
Colour Contrast Analyser |
— |
| Анимации при reduce motion |
— |
Settings → Accessibility |
Сравнение инструментов аудита
| Инструмент |
Скорость |
Глубина анализа |
Цена |
| Accessibility Inspector |
быстрый |
базовая |
бесплатно |
| Accessibility Scanner |
быстрый |
базовая |
бесплатно |
| axe DevTools Mobile |
средняя |
полная WCAG |
коммерческая |
Процесс работы
- Аудит — прогон через Accessibility Inspector и Scanner, получение списка нарушений с приоритизацией.
- Автоматизация — добавляем
AccessibilityChecks.enable() в Espresso-тесты, чтобы каждое действие проверяло a11y.
- Ручная проверка — проходим ключевые сценарии с VoiceOver и TalkBack, фиксируем семантические проблемы.
- Отчёт — документируем все нарушения с классификацией по WCAG 2.1 (A/AA) и рекомендациями по исправлению.
Что входит в работу
Мы предоставляем подробный отчёт с найденными нарушениями и их критичностью, рекомендации по исправлению для каждого типа ошибок, обновлённые автотесты с включёнными accessibility-проверками, краткую инструкцию для команды (iOS/Android) по предотвращению регрессов, а также сертификат соответствия WCAG 2.1 AA (по запросу). Согласно WCAG 2.1 Success Criterion 1.4.3, минимальный коэффициент контрастности для обычного текста — 4.5:1.
Сроки и стоимость
Аудит среднего приложения занимает от 2 до 4 дней. Полная документация для EU Accessibility Act может добавить ещё 2 дня. Стоимость рассчитывается индивидуально, исходя из объёма экранов и сложности сценариев. Свяжитесь с нами — мы оценим ваш проект и предложим оптимальный план. Закажите консультацию по доступности сегодня.
Частые ошибки при внедрении доступности
- Отсутствие контраста у плейсхолдеров и подсказок — часто используют серый #999999 на белом.
- Мелкие кнопки в навигационных панелях и тулбарах — меньше 44pt/48dp.
- Игнорирование жестов — если есть swipe-to-delete, нужно дублировать через длительное нажатие.
Тестирование мобильных приложений: XCTest, Espresso, Detox и Appium
Flaky-тест, падающий на CI раз в пять запусков без воспроизводимой причины, хуже его отсутствия. Команда перестаёт доверять инфраструктуре и отключает тесты — регрессии проскакивают в продакшн. Мы это видим каждый день и знаем, как выстроить надёжную систему тестирования, которая не требует постоянного внимания. Получите консультацию — оценим ваш проект и предложим архитектуру тестов под ваш стек.
Почему flaky-тесты опасны?
Одна нестабильная проверка может завалить пайплайн, заблокировав релиз. Разработчики тратят 15-20% рабочего времени на перезапуск и анализ ложнонегативных сбоев. Автоматизация без стабильности — не экономия, а потеря эффективности. Мы решаем эту проблему на уровне архитектуры: Gray Box-фреймворки (Detox, Patrol) синхронизируются с состоянием приложения, а native инструменты (XCUITest, Espresso) получают правильные IdlingResource и accessibilityIdentifier. Результат: стабильность >99% на CI.
Unit-тесты: что тестировать, а что нет
На iOS XCTest — основа. Бизнес-логика в ViewModel, Interactor, UseCase — тестируется без проблем, если она не тянет UIKit. Типичная ошибка: логика в UIViewController напрямую — тогда unit-тест требует создания view-иерархии, что медленно и нестабильно. Выход — выносить логику в сервисы с @testable import.
Для асинхронного кода в Swift: XCTestExpectation для старого стиля, await + XCTest async для современного. С Combine — XCTestExpectation + sink, но удобнее использовать библиотеки типа CombineExpectations. На Android JUnit 4/5 + Mockito для unit-тестов, Coroutines Test для suspend-функций. runTest {} из kotlinx-coroutines-test — стандарт для ViewModel с StateFlow. Покрытие кода unit-тестами на уровне 80% сокращает время регрессии на 60% (данные наших проектов).
UI-тесты: стабильность важнее покрытия
XCUITest (iOS) и Espresso (Android) — нативные UI-тесты. Работают быстро, интегрированы с IDE, но тестируют одну платформу. Главная проблема XCUITest — хрупкость селекторов. app.buttons["Войти"] падает при смене локализации или рефакторинге accessibility label. Правильный подход: accessibilityIdentifier для тестируемых элементов, никогда не текстовые метки. Идентификаторы из shared enum — чтобы они не расходились между приложением и тестами. Опыт показывает: такая практика снижает flakiness на 90%.
Espresso на Android стабильнее из-за IdlingResource механизма — тест автоматически ждёт завершения background операций. Но кастомные async операции (OkHttp, кастомные Executors) нужно регистрировать в IdlingRegistry вручную, иначе тест не синхронизируется с сетевыми запросами. Мы гарантируем правильную настройку IdlingResource на этапе аудита.
Detox и Patrol: end-to-end для React Native и Flutter
Detox — E2E фреймворк для React Native, разработанный Wix. Работает на реальных устройствах и симуляторах через Gray Box подход: знает о состоянии JS thread и синхронизируется с ним. Это решает главный flakiness-источник — тест не нажимает кнопку, пока приложение занято. Настройка Detox нетривиальна. Требует специальный debug-билд с DetoxInstrumentsServer, конфигурации в package.json и отдельного Appium-сервера не нужно. Типичная проблема: тест стабилен на симуляторе, падает на реальном устройстве из-за анимаций. Решение — animations: disabled в Detox конфигурации для E2E билда.
Patrol — аналог для Flutter. Расширяет встроенный integration_test пакет и добавляет возможность взаимодействовать с нативными системными диалогами (permission prompts, notifications) — то, что flutter_driver и базовый integration_test не умеют. Для CI используется через patrol test --target integration_test/app_test.dart.
Appium: кроссплатформа с ценой
Appium — когда нужно покрыть iOS и Android одними тестами. Использует WebDriver протокол, поверх XCUITest и UiAutomator2 драйверов. Скорость ниже нативных фреймворков, но для команд без ресурсов на две тестовые кодовые базы — компромисс. Appium 2.x с плагинной архитектурой заметно удобнее первой версии. appium-doctor диагностирует окружение — полезен при настройке CI.
CI и параллелизация
Для параллельного запуска XCUITest используем Xcode Cloud или xcodebuild test-without-building с несколькими симуляторами через parallel-testing-enabled. Время прогона 200 UI-тестов с параллелизацией на 4 симулятора — с 40 минут до 12. На Android аналогично используем Firebase Test Lab с шардингом.
| Фреймворк |
Платформа |
Gray Box |
Скорость |
Системные диалоги |
| XCUITest |
iOS |
Нет |
Высокая |
Да (через addUIInterruptionMonitor) |
| Espresso |
Android |
Да (IdlingResource) |
Высокая |
Ограничено |
| Detox |
React Native |
Да |
Средняя |
Ограничено |
| Patrol |
Flutter |
Частично |
Средняя |
Да |
| Appium |
iOS + Android |
Нет |
Низкая |
Да |
Типичные ошибки при настройке (и как их избежать)
| Ошибка |
Последствие |
Решение |
| Использование текстовых меток в селекторах |
Тесты падают при локализации |
accessibilityIdentifier из enum |
| Отсутствие IdlingResource для кастомных Executor |
Espresso не ждёт ответа сервера |
Регистрация в IdlingRegistry |
| Включённые анимации на реальном устройстве в Detox |
Flaky тесты из-за таймингов |
animations: disabled в E2E билде |
| Параллелизация без изоляции состояния |
Гонки данных между тестами |
Запуск каждого теста в свежем симуляторе |
Как мы это делаем: процесс работы
-
Аудит текущего кода и CI — оцениваем flakiness, покрытие, узкие места.
-
Проектирование тестовой архитектуры — выбираем фреймворк, селекторы, моки.
-
Настройка инфраструктуры — CI пайплайн, parallel execution, отчёты (Allure, Xcode Report).
-
Написание тестов — unit, UI, E2E, performance (XCTMetrics, Macrobenchmark).
-
Интеграция и стабилизация — прогон 200+ тестов, отлов flaky-кейсов.
-
Передача документации — архитектура, запуск, troubleshooting.
Что входит в работу (deliverables)
- Архитектурная документация тестового покрытия
- Настроенный CI-пайплайн с параллелизацией и отчётами
- Код тестов (unit, UI, E2E) с styleguide
- Обучение команды (2 часа воркшопа)
- Доступ к тестовым билдам и CI-логам
- Поддержка в течение месяца после сдачи (фикс flakiness, обновление под новые версии)
Сроки ориентировочно
Настройка инфраструктуры с нуля (CI, unit + UI тесты, отчёты) — 2-3 недели. Написание покрытия для существующего приложения — от 2 недель до месяца в зависимости от объёма. Оценим ваш проект за 2 дня — свяжитесь с нами. 5+ лет опыта в автоматизации, 50+ успешных проектов, сертифицированные специалисты по iOS/Android. Гарантируем стабильность тестов >98% на CI после внедрения.