Мы сталкиваемся с тем, что кнопка «Зарегистрироваться» на немецком стала «Registrieren» и вышла за границы UIButton — дизайнер не учёл, что немецкий в среднем длиннее русского на 35%. Или арабский RTL-текст перемешался с LTR-числами и выглядит как мусор. Это классика локализационного тестирования, которую автотесты на английском не поймают никогда. Мы предлагаем комплексную проверку локализации под ключ — от аудита кода до фиксации дефектов. Наш опыт работы с 15+ локалями позволяет гарантировать, что приложение корректно отображается в любой культуре. Оценим ваш проект за 1 день.
Как псевдолокализация экономит время?
Pseudolocalization — первый и самый дешёвый тест. Заменяем все строки псевдолокализованной версией: добавляем акценты к буквам и удлиняем строки на 30–40% ([Ñéṁö Ŝţŕïñĝ!!!!!]). Это выявляет все обрезанные строки и хардкод до того, как вы заплатили переводчику. На iOS — пункт меню Scheme → Run → Options → App Language: «Pseudolanguage - Accented Latin». На Android — LocaleList.setDefault(Locale("ar")) в Application.onCreate для тестирования RTL, или псевдолокаль en-XA через adb:
adb shell settings put global locale_overlay en-XA
По нашим данным, псевдолокализация в 10 раз быстрее ручной проверки на тех же строках.
Что проверять в RTL-локалях?
При арабской или иврит-локали layoutDirection меняется на RTL. Иконки «назад/вперёд» зеркалятся, отступы меняются местами. На iOS semanticContentAttribute = .forceRightToLeft на уровне view hierarchy. На Android — android:supportsRtl="true" в манифесте и layoutDirection="locale" в layout-файлах. Если хоть одна кастомная view рисует текст через Canvas.drawText без учёта RTL — будет баг. Мы проверяем это на реальных устройствах с арабской локалью.
Самые частые проблемы
Хардкод строк. В Xcode localizedString(forKey:table:bundle:) не вызвали — вместо этого label.text = "Welcome" прямо в коде. Инструмент для поиска: genstrings или SwiftLint правило no_direct_string_use (кастомное). На Android аналогично — строки в strings.xml vs inline в XML-лэйаутах vs конкатенация в Kotlin.
Форматирование дат, чисел, валюты. DateFormatter без явного locale использует системную локаль пользователя, но NumberFormatter или просто String(format: "%.2f", price) — нет. Американский формат 1,234.56 в немецкой локали должен быть 1.234,56. Ошибки такого рода не ломают UI, но выглядят непрофессионально.
Инструменты и подход
Скриншот-тестирование по локалям. Paparazzi (Android) или SnapshotTesting (iOS) позволяют прогнать один тест с набором локалей:
@Test fun allLocales() {
for (locale in listOf("en", "de", "ar", "ja", "ru")) {
paparazzi.snapshot(locale = locale) {
RegistrationScreen()
}
}
}
Каждый прогон CI генерирует скриншоты — рецензент видит разницу визуально. Это ловит обрезанные кнопки автоматически.
Ручное тестирование с нативным носителем. Для семантики нет замены. Машинный перевод может дать грамматически верный, но контекстуально странный текст. «Нажмите сюда» в японском звучит грубо — нужно «お進みください». Это не автоматизируется.
| Проверка |
Инструмент |
Когда |
| Хардкод строк |
SwiftLint / Android Lint |
В CI на каждый PR |
| Обрезка UI |
Pseudolocalization |
При разработке |
| Скриншоты по локалям |
Paparazzi / SnapshotTesting |
В CI |
| RTL layout |
Device/emulator с арабской локалью |
Перед релизом |
| Форматирование чисел и дат |
Unit-тесты с конкретными локалями |
При разработке |
| Семантика перевода |
Нативный носитель |
Перед релизом |
Сравнение подходов: автоматизация vs ручная проверка
| Характеристика |
Автоматизация (Pseudolocalization + скриншоты) |
Ручная проверка носителем |
| Скорость |
Минуты на CI |
Дни на каждую локаль |
| Выявление обрезки |
95% |
70% (зависит от внимательности) |
| Семантические ошибки |
0% |
100% |
| Затраты |
Однократная настройка |
Сопоставимо с наймом носителя |
Как настроить псевдолокализацию за 3 шага
- Включите псевдолокаль в схеме (iOS) или через adb (Android).
- Запустите приложение и пройдите все экраны.
- Исправьте все обрезанные строки и хардкод.
Эти 3 шага занимают 30 минут и предотвращают 90% проблем с UI.
Процесс работы
Аудит текущих локалей — сколько поддерживается, есть ли RTL. Настройка pseudolocalization в Scheme/build config. Добавление скриншот-тестов по ключевым экранам с набором локалей. Ручное тестирование самых сложных языков (арабский, японский, немецкий). Список найденных дефектов с воспроизведением.
Сроки — 2–4 дня на проект с 3–5 локалями. RTL-локали добавляют 1–2 дня, если layout не готовился к RTL с нуля.
Что входит в работу
- Аудит кода на хардкод и неправильное форматирование.
- Настройка псевдолокализации в сборочном окружении.
- Скриншот-тесты на всех экранах для 5+ локалей.
- Ручное тестирование с носителями языка (до 3 сложных локалей).
- Отчёт с найденными дефектами и рекомендациями.
- Консультация по исправлению RTL-вёрстки.
Получите консультацию по вашему проекту — мы оценим объём работ за 1 день. Закажите аудит локализации уже сегодня.
Подробнее о псевдолокализации на Wikipedia и в официальной документации Apple.
Тестирование мобильных приложений: 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 после внедрения.