Тестирование мобильных приложений: эмуляторы, симуляторы и реальные устройства
Вы только что закончили сборку мобильного приложения. Оно отлично работает на вашем ноутбуке, билд подписан и готов. Вы уверены, что можно отправлять его в магазин. Но есть неприятная правда: ваш ноутбук — это не телефон пользователя. Даже близко.
Пользователи запускают ваше приложение на сотнях разных устройств. Разные размеры экранов, версии ОС, уровни заряда батареи, сетевые условия и конфигурации железа. Баг, который никогда не проявляется на вашей рабочей машине, может положить приложение на конкретной модели телефона. Один неудачный релиз способен обрушить рейтинг в магазине и за одну ночь лишить вас пользователей.
Тестирование мобильных приложений — не опция. Но то, как вы тестируете и где запускаете тесты, сильно влияет на уверенность в качестве перед релизом.
Уровни тестирования, которые имеют значение
Мобильное тестирование — это не что-то одно. Это стопка уровней, каждый из которых ловит разные проблемы с разной скоростью.
Модульные тесты находятся внизу. Они проверяют, что отдельный элемент поведения работает корректно. В контексте мобильной разработки это означает тестирование через значимую точку входа: правильно ли ViewModel реагирует на нажатие кнопки? Возвращает ли use case правильный результат для заданных входных данных? Вы не тестируете внутренние детали реализации. Вы тестируете наблюдаемое поведение. Модульные тесты работают быстро, часто за миллисекунды. Их можно запускать при каждом изменении кода в пайплайне, используя эмулятор или симулятор. Физическое устройство не нужно.
Интеграционные тесты находятся уровнем выше. Они проверяют, как компоненты работают вместе. Корректно ли отображаются данные из API на экране? Работает ли локальное хранилище после входа пользователя? Для этих тестов нужно окружение, похожее на реальное устройство. Эмуляторы, симуляторы или настоящие устройства — все подходит, в зависимости от того, что вы интегрируете.
UI-тесты находятся наверху. Они симулируют реальные действия пользователя: нажатие кнопок, заполнение форм, прокрутку списков и проверку, что ожидаемые элементы появились на экране. Эти тесты максимально приближены к тому, что видят ваши пользователи. Они же самые медленные и хрупкие. Небольшое изменение верстки может их сломать. Но когда они проходят, вы знаете, что приложение работает с точки зрения пользователя.
У каждого уровня свое место. Модульные тесты дают быструю обратную связь. Интеграционные тесты ловят проблемы связки. UI-тесты проверяют пользовательский опыт. Вам нужны все три, но не обязательно запускать их все при каждом триггере.
Эмуляторы и симуляторы: быстро, но не идеально
Эмуляторы (Android) и симуляторы (iOS) — рабочая лошадка мобильного тестирования. Они бесплатны, легко поднимаются в CI/CD пайплайне и достаточно хороши для большинства проверок логики и верстки.
На них можно запускать модульные тесты, интеграционные и даже UI-тесты. Они быстро загружаются, поддерживают разные версии ОС и позволяют симулировать различные размеры экранов. Для ежедневной разработки и внутренних сборок их обычно достаточно.
Но у них есть слепая зона. Эмуляторы и симуляторы не воспроизводят поведение реального устройства. Производительность другая. Потребление батареи другое. Реакция сенсоров другая. Поведение сети в сотовом режиме другое. Тест, который идеально проходит на эмуляторе, может упасть на физическом устройстве из-за таймингов, нехватки памяти или особенностей железа.
Чтобы сделать это практичным, вот job для GitHub Actions, который создает эмулятор Android, ждет его загрузки, запускает инструментированные тесты и собирает результаты:
name: Android Instrumented Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Create and start emulator
run: |
echo "no" | avdmanager create avd -n testDevice -k "system-images;android-33;google_apis;x86_64" --force
$ANDROID_HOME/emulator/emulator -avd testDevice -no-window -no-audio &
- name: Wait for emulator to boot
run: |
adb wait-for-device
adb shell settings put global window_animation_scale 0.0
adb shell settings put global transition_animation_scale 0.0
adb shell settings put global animator_duration_scale 0.0
- name: Run instrumented tests
run: ./gradlew connectedCheck
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: app/build/reports/androidTests/connected/
Если вы тестируете только на эмуляторах, вы выкладываетесь с неполной информацией.
Фермы устройств: реальное железо в масштабе
Здесь в игру вступают фермы устройств. Это сервисы, которые дают доступ к настоящим телефонам и планшетам, стоящим в дата-центре. Вы загружаете приложение, запускаете тесты на десятках устройств одновременно и получаете отчет о том, что прошло, а что сломалось на каждом устройстве.
Популярные варианты: Firebase Test Lab (Android и iOS) и AWS Device Farm. Эти сервисы поддерживают разные модели устройств, версии ОС и размеры экранов. Их можно интегрировать прямо в ваш CI/CD пайплайн. Каждый раз, когда сборка завершается, пайплайн запускает тесты на реальном железе до того, как приложение попадет в магазин.
Фермы устройств ловят проблемы, которые пропускают эмуляторы. Падения на конкретном железе, проблемы верстки на нестандартных соотношениях экрана, деградация производительности на старых устройствах. Они также предоставляют скриншоты и логи с каждого тестового запуска, что упрощает диагностику ошибок.
Но фермы устройств не бесплатны. Они стоят денег за каждый тестовый запуск, и тесты выполняются дольше, чем на эмуляторах. Не стоит прогонять каждую сборку через ферму устройств — это будет медленно и дорого.
Когда что использовать
Решение практическое, а не идеологическое. Используйте эмуляторы и симуляторы для быстрой обратной связи во время разработки и для внутренних сборок. Используйте фермы устройств стратегически, перед релизами.
Следующая блок-схема обобщает рекомендуемое окружение для каждого типа тестов в зависимости от потребностей в скорости и точности:
Вот простое эмпирическое правило:
- Каждый коммит: запускайте модульные тесты на эмуляторе или симуляторе.
- Каждый pull request: запускайте модульные и интеграционные тесты на эмуляторе или симуляторе.
- Перед staged rollout или поэтапным релизом: запускайте полный набор тестов на ферме устройств, покрывая репрезентативный набор девайсов.
Ключ в том, чтобы соотносить интенсивность тестирования с риском. Небольшой фикс бага во внутренней функции не требует полного прогона на ферме устройств. Релиз, который увидят тысячи пользователей, — абсолютно точно требует.
Практический чек-лист перед отправкой в магазин
Прежде чем нажать кнопку отправки в панели управления магазином приложений, пройдитесь по этому чек-листу:
- Модульные тесты проходят на эмуляторе или симуляторе для целевых версий ОС.
- Интеграционные тесты проходят для основных пользовательских сценариев (вход, отображение данных, навигация).
- UI-тесты проходят как минимум на одном эмуляторе и одном физическом устройстве.
- Тесты на ферме устройств проходят для топ-5-10 моделей, которые реально используют ваши пользователи по данным аналитики.
- Нет отчетов о падениях или ANR (application not responding) после прогона на ферме устройств.
- Скриншоты с фермы устройств соответствуют ожидаемой верстке на разных размерах экранов.
Этот чек-лист не исчерпывающий, но он ловит самые частые проблемы, которые проскальзывают в продакшн.
Конкретный вывод
Тестирование только на эмуляторах — это как тест-драйв автомобиля на парковке. Вы знаете, что руль работает, но не знаете, как машина ведет себя на трассе. Фермы устройств дают вам данные с трассы. Используйте эмуляторы для скорости, а фермы устройств — для уверенности. Автоматизируйте и то, и другое в вашем пайплайне — и вы будете выкатывать меньше багов и лучше спать по ночам.