Привет, Хабр! Я Алексей Булахов, инженер по обеспечению качества в Tele2. Одним из самых нетривиальных вопросов, которым может задаться QA-инженер, который планирует тестировать функционал мобильного приложения, является выбор среды для тестирования. Это могут быть реальные или виртуальные девайсы (эмуляторы/симуляторы) или же готовые решения (различные облачные платформы). Для нас этот вопрос тоже был весьма важен, и на разных этапах становления проекта нам довелось поработать со всеми вышеизложенными вариантами, так как среди них нет идеальных решений и каждый вариант хорош в рамках определенного контекста. Об этом я и расскажу далее.
Условно историю нашего проекта можно разделить на три этапа становления, когда преследовались разные цели.
Этап 1. Переход на облачные платформы
В начале проекта мы определили ключевые для нас критерии оценки сред тестирования и провели аналитическую работу. В результате каждой среде была дана характеристика по каждому из критериев.
Таблица 1. Оценка сред тестирования
Характеристика/Среда |
Облачные платформы |
Реальные девайсы |
Виртуальные девайсы |
Простота первичной настройки и подключения |
Просто |
Умеренно |
Сложно |
Первоначальные траты |
Низкие |
Большие |
Большие |
Траты в долгосрочной перспективе |
Большие |
Умеренные |
Низкие |
Дополнительные затраты в процессе эксплуатации |
Иногда (при использовании функций за дополнительную плату) |
Есть (быстрый износ реальных девайсов) |
Нет |
Стоимость масштабирования в дальнейшем |
Большая |
Большая |
Низкая |
Гибкость настройки |
Умеренно гибкая |
Не гибкая |
Гибкая |
Функциональность |
Ограниченный функционал девайса |
Полный функционал девайса |
Ограниченный функционал девайса |
Стабильность |
Умеренно стабильное решение |
Стабильное решение |
Стабильное решение |
Мы определили, что на начальных этапах облачные решения являются лучшим вариантом. Их достаточно просто и быстро настраивать, они относительно дешевы в краткосрочной перспективе (так как не нужно покупать целиком ферму, достаточно лишь купить к ней подписку) и обладают достаточно большим функционалом, чтобы с их помощью проверять огромное количество кейсов. Однако мы понимали и слабые стороны этого решения: большие расходы при долгосрочном использовании и при масштабировании (увеличении количества девайсов), а также ограниченная функциональность, которая не позволяет проводить ряд тестов (например, в облаке нельзя протестировать сеть с заранее настроенными SIM-картами).
В итоге мы решили начать тестирование функциональности с облаков, чтобы минимизировать время на настройку среды. Со временем при необходимости масштабирования это позволило бы развернуть фермы реальных или виртуальных девайсов, чтобы минимизировать издержки.
Этап 2. Переход на фермы реальных девайсов
Подключение и настройка облачных ферм были достаточно простыми. Благодаря этому мы почти сразу смогли приступить к непосредственным задачам – автоматизации тестирования и проверке качества работоспособности функционала мобильного приложения. Ни эмуляторы, ни облачные сервисы не могли предоставить нам функционал, позволяющий протестировать сеть на реальных SIM-картах с нужными конфигурациями. Поэтому после реализации нескольких сотен кейсов у нас появилась потребность в создании фермы реальных девайсов. Реализация этого решения была куда сложней, чем в случае с облачными платформами, так как требовалось не только подключиться к сервису, но и создать в целом этот сервис с нуля (ферму реальных девайсов). В результате были созданы две фермы девайсов (для iOS и для Android). Первая была реализована на базе macOS с последующим подключением к нему iOS-девайсов, вторая – на базе Linux с последующим подключением к нему Android-девайсов.
Этап 3. Переход на фермы виртуальных девайсов
После реализации этих ферм было проверено еще несколько сотен кейсов, и появились новые проблемы. Во-первых, облачные платформы оказались не такими стабильными, как мы изначально ожидали. Время от времени бывали какие-то сбои, из-за чего происходили отвалы тестов (такое часто бывало по ночам и в выходные дни). Подобных проблем не было на реальных девайсах. Но и с ними было не все гладко. Хотя у них не было подобных проблем, имелась другая, не менее важная. Так, при очень частом выполнении автотестов на реальных девайсах их срок службы оставлял желать лучшего. В связи с этим постоянно прогонять большое количество тестов оказалось экономически невыгодно.
Требования к решению были таковы: умеренные финансовые затраты на поддержание и масштабирование, стабильность и гибкость в настройке. Это позволило бы сократить до минимума количество автотестов на облачной ферме и ферме реальных девайсов. И такое решение было – виртуальные девайсы.
Для их реализации тоже требовалось создание двух ферм (на базе macOS и Linux). Эти фермы были очень схожи с фермами реальных девайсов. Разница заключалась лишь в том, что вместо реальных девайсов должны были быть развернуты эмуляторы и симуляторы. Еще одно отличие – сами фермы должны иметь бОльшую мощность, чтобы поднять большое количество эмуляторов и симуляторов.
Мы разобрались с тем, почему сместили акцент на фермы виртуальных девайсов, но не разобрались, как мы реализовывали данные решения.
На облачных фермах мы останавливаться не будем, так как каждая облачная ферма – это готовая реализация, которая имеет подробную документацию на все случаи жизни. Она расскажет о том, как настроить такую ферму куда лучше нас.
Разница между фермой реальных девайсов и виртуальных заключается лишь в самих девайсах. Вся остальная архитектура остается неизменной. Ниже мы остановимся на архитектуре виртуальных девайсов (так как она чуть более сложная). Если вам интересна архитектура реальных, на всех диаграммах вместо докера с поднятым эмулятором можно представить реальный девайс, который по кабелю подключен к серверу – и всё, схема готова.
На ферме виртуальных девайсов остановимся поподробней.
У нас их две: одна – для iOS, другая – для Android. Поскольку на момент написания статьи ферма iOS-симуляторов еще не до конца реализована, все диаграммы и описания будут касаться фермы Android. В связи с тем, что ферма заточена под наш проект, рассматривать ферму будем в контексте проекта, чтобы было понятно, для чего реализованы те или иные механизмы внутри фермы.
Ферма эмуляторов (рис. 1) представляет собой сервер с ОС Linux, на котором стоит Docker, ADB и Appium. Подготовка и настройка среды выполняются автоматически при старте сервера с помощью bash-скриптов, которые выполняют следующие задачи:
Запуск Appium.
Запуск требуемого количества контейнеров для mock-тестирования. На этих эмуляторах прописывается прокси на mitmproxy, который, в свою очередь, делегирует запросы либо на production либо в wiremock в зависимости от установленной конфигурации проксирования. Подробней на этом моменте остановимся чуть позже.
Запуск требуемого количества контейнеров для production-тестирования (на этих эмуляторах прокси отсутствует, и все запросы уходят в production-среду).
Установка всех необходимых сертификатов на эмуляторы.
Установка мобильного приложения на эмуляторы.
Итак, после запуска фермы у нас автоматически поднимаются Appium и контейнеры с эмуляторами (мы используем общедоступный образ androidsdk/android-30, который располагается на dockerhub). На запущенные виртуальные девайсы ставятся сертификаты, необходимые для корректного взаимодействия с mitmproxy, устанавливаются прокси (при необходимости) и мобильное приложение. После этого девайсы готовы к тестированию.
Наверняка у вас остаются вопросы: зачем на ферме установлен Appium, для чего устанавливаются сертификаты mitmproxy и зачем он вообще нужен? Ответ достаточно прост – это специфика нашего проекта, и наша ферма заточена под технологии, используемые на проекте автотестирования. Именно поэтому невозможно рассматривать ферму в вакууме, понимание придет, только если делать это в контексте нашего проекта. Тогда взаимодействие будет очевидным.
Рассмотрим бегло наш проект и выстроенный CI-процесс (рис. 2). У нас есть сервер с Jenkins как инструментом непрерывной интеграции, который позволяет разворачивать докер-контейнер с нашим проектом при запуске Jenkins Pipeline. Абстрагируясь от деталей, можно представить работу теста в рамках проекта следующим образом:
Поднимается wiremock: он представляет собой HTTP-сервер с заранее заготовленными заглушками, которые возвращаются в ответ на запросы. После поднятия сервера на определенном порту прописывается вся необходимая конфигурация и добавляются заглушки, требуемые для текущего теста.
Поднимается mitmproxy, который позволяет перехватывать, инспектировать, модифицировать и повторно воспроизводить поток трафика. Конфигурация MITM описана в виде python-скриптов. Она представляет собой набор регулярных выражений, на основании которых приходящий запрос уходит или в wiremock для получения заглушки, или в production-среду.
Поднимается Appium Driver, который создает сессию с Appium Remote Server для делегирования ему всех полученных команд. Appium Remote Server после получения делегированных команд выполняет их с помощью UIAutomator2-драйвера на эмуляторе. Appium как инструмент помогает исполнять команды на конечном устройстве с помощью специального драйвера, позволяющего это сделать на данном типе устройств.
Начинается выполнение Junit-теста.
Тест состоит из набора команд, которые должны выполняться в мобильном приложении на девайсе, и проверок, насколько данные действия соответствуют ожидаемому результату. Рассмотрим детально, как будет выполняться mock-тест:
-
Выполняется шаг теста: в его рамках необходимо выполнить какое-то действие или группу действий, которые должны изменить состояние приложения: например, запуск приложения, клики по объектам, ввод текста. Данные команды передаются Appium Driver, который делегирует их Appium Remote Server. Он, в свою очередь, выполняет эти действия на эмуляторе (рис. 3).
-
После выполнения команд состояние приложения изменилось. Информация о новом состоянии приложения возвращается в тест зеркальным образом. Эта информация при необходимости сверяется с ожидаемым результатом, на основании чего данный шаг помечается как успешный или проваленный (рис. 4).
-
При изменении состояния приложения иногда требуется отправлять запросы на различные API для корректной работы функционала или отрисовки контента. В случае mock-тестирования на эмуляторах настроены прокси, которые перенаправляют все в mitmproxy. Mitmproxy сверяет полученный с запрос с регулярными выражениями, которые имеются на основании конфигурационных скриптов. Исходя их этих сопоставлений принимается решение о том, куда перенаправить трафик для получения ответа – в wiremock или другое место, например, uat или production-среду (Рис. 5).
-
Если запрос перенаправляется в wiremock, он сопоставляется со списком заранее добавленных заглушек. Если на такой запрос есть соответствующая заглушка, она возвращается в качестве ответа назад по цепочке мобильному приложению. На основании полученных данных приложение может корректно изменить свое состояние (рис. 6).
Дальше алгоритм повторяется циклично до тех пор, пока все шаги теста не будут пройдены.
В результате построения такой архитектуры удалось построить легко масштабируемый процесс тестирования с помощью эмуляторов. Помимо экономических соображений, эмуляторы имеют важное преимущество относительно реальных девайсов: они могут крайне гибко настраиваться. Можно детально настроить аппаратные мощности, выделенные для девайса, выбрать необходимый размер экрана и версию ОС, установить любую оболочку, что, несомненно, полезно для тестирования. Создание фермы эмуляторов позволило нам перевести большую часть тестов «с небес на землю». Почему мы не перевели все тесты на эмуляторы? На это есть две причины:
Некоторые тесты невозможно выполнить на эмуляторах (точно так же, как и при использовании облачных решений). Эти тесты выполняются на реальных девайсах.
На момент написания статьи ферма симуляторов iOS находится еще на этапе пилота и полноценно не функционирует, в связи с чем часть тестов iOS до сих пор выполняется на облачных платформах. В будущем планируется полный переход от облачных ферм к собственным фермам реальных и виртуальных девайсов.
В итоге мы можем наблюдать, что все три среды тестирования имеют право на существование, и идеального решения среди них нет. Важно осознавать плюсы и минусы каждого из решений и то, насколько целесообразно их использовать в контексте текущих целей проекта.
В нашем случае мы не смогли остановиться на одном единственном решении, хотя акцентировали внимание больше на фермы виртуальных девайсов. А какое решение для вас оказалось лучшим в рамках вашей компании и ваших задач? И почему? Будет интересно узнать разные точки зрения.
sicvense
В чем смысл тестирования приложения с разными sim? Приложение же использует транспортный уровень только, и не взаимодействует напрямую с sim? Было бы интересно про это послушать если с sim работаете каким то образом. А если только транспортный, то этот уровень предоставляется ОС и эмулировать задержки на сети, джиттер, обрывы, потерю пакетов так же можно в любой ОС.