Про тестирование мобильных приложений. Часть 3. Cквозное (UI, e2e) тестирование |
29.05.2023 00:00 |
Автор: Виталий Никоноров
Ранее мы с вами познакомились с пирамидой тестирования и ее основанием. В данной же статье предлагаю перейти к сразу к вершине пирамиды. На вершине пирамиды, представленной в статье 1, расположены сквозные тесты. В контексте сквозных тестов, речь может идти об e2e (end-to-end), UI, системных, тестах пользовательского интерфейса... Иными словами в данной статье речь пойдет о тестах, которые проводятся над системой, как над единым целым. Основная задача этой группы тестов - проверка того, удовлетворяет ли вся система, как единое целое, представленным и заявленным требованиям. В разработке мобильных приложений в целом, как и в разработке приложений под android в частности, понятие e2e тесты часто используется как синоним понятий тесты пользовательского интерфейса (UI тесты) и инструментальные тесты. Однако следует помнить, что в общем случае это не совсем так, поскольку они не всегда означают одно и то же, так как все может зависеть от контекста. Инструментальные тесты – это тесты, для выполнения которых требуется специальная среда – либо физически подключенное устройство (смартфон, планшет и т.п.), либо эмулятор (симулятор). Данная группа может не ограничиваться привычными UI тестами, но может также включать в себя большое множество других видов тестирования - например тестирование работы с базой данных или диском, в которых UI вовсе не нужен, screenshot тесты и т.д. E2E (сквозные) тесты, в свою очередь могут включать в себя:
Таким образом, при использовании данных терминов всегда следует помнить о контексте. Далее в данной статье в основном речь пойдет об инструментальных UI тестах. Итак, UI тесты, как подмножество инструментальных тестов, выполняются на устройствах с операционной системой таргетируемой платформы (реальной или эмулированной), благодаря чему в них может использоваться практически все, что предоставляет API платформы. UI тесты для мобильных приложений - это автотесты, симулирующие взаимодействие пользователя с приложением. Эта группа тестов позволяет проконтролировать, что элементы пользовательского интерфейса работают так, как предполагает сценарий. Чаще всего такие тесты пишутся с использованием фреймворков. Для Android в основном это Espresso и UI Automator. Однако так же бывают и свои проприетарные решения с использованием сторонних технологий (например Jest). API фреймворков может быть не всегда удобным и читаемым в силу ряда причин, и к ним зачастую пишутся свои обертки и библиотеки для более удобной работы (например Kakao, Kaspresso). Наиболее распространенной практикой при написании UI тестов является применение паттерна Page Object. Основная идея подхода заключается в создании объектно-ориентированного представления экрана/страницы (изначально паттерн применялся при тестировании web-страниц), с которой и взаимодействует тест. Такое представление позволяет отвязать тесты от деталей реализации страницы и писать более качественные тесты, которые намного проще писать и поддерживать. Пример такого представления для приложения, описанного в статье 1:
Пример самого теста:
Пример запуска: Пользу данного подхода невозможно переоценить, в случае изменения интерфейса экрана. В таком случае будет необходимо лишь изменить один page object вместо переписывания всех существующих тестов. Как видим, сам тест не привязан к конкретной реализации экрана: К тому же, если такие представления будут написаны аналогичным образом на других платформах (iOS/Web) можно прибегнуть к автоконвертации кода, что позволит QA-инженеру переиспользовать тесты между платформами и многократно повысят их продуктивность. Как можно заметить, в противоположность к unit-тестам, тесты пользовательского интерфейса, и тем более e2e тесты, включают в себя большое количество компонентов и систем. Что с одной стороны также позволяет нам протестировать все эти системы и их взаимодействие написав лишь один тест. Однако из этого также вытекает и их основной недостаток - из-за наличия большого количества компонентов, которые могут влиять на результат выполнения – такие тесты не всегда стабильны. Подобными вопросами занимается такая область науки как Теория надёжности, которая определяет надежность, как свойство объекта (системы) сохранять во времени в установленных пределах значения всех параметров, характеризующих способность выполнять требуемые функции в заданных условиях применения, технического обслуживания, хранения и транспортирования. Если для простоты принять, что каждая команда в нашей системе проходит по системе с последовательным соединением (в реальности все конечно же намного сложнее): эмулирование нажатия на экран, обработка нажатия операционной системой, обработка нажатия кодом приложения, сетевой запрос/ответ, обработка ответа приложением, выработка команды обновления пользовательского интерфейса приложения, обработка OS полученной команды, аппаратное обновление интерфейса, обработка измененного состояния интерфейса тестовым фреймворком… То можно применить формулу [1] умножения вероятностей: То есть надежность нашей системы есть произведение надежностей элементов ее составляющих. Идеальных же компонентов не существует, и если предположить что наша система состоит из 10 элементов, надежность каждого из которых 99%, получим Что можно трактовать так, что каждый десятый запуск теста будет заканчиваться неудачей. Из-за чего команде придется часто проверять, реально ли что-то сломалось, либо же результат ложноотрицательный. Что, согласитесь, весьма плохо и будет невероятно сильно тормозить разработку и демотивировать команду (мы ведь все хотим писать новые фичи, которые мы можем добавить в наше performance review, а не править фантомные баги). Помимо нашего кода, на результат выполнения теста также сильное влияние может оказывать его окружение – состояние эмулятора, либо устройства, состояние сети (если она используется). Также выполнение UI тестов невероятно медленное и ресурсоемкое. К примеру выполнение теста приведенного выше на моем устройстве заняло 2 секунды, в то время как прогон unit тестов занимает доли секунды. Как можно заметить, проблем с данным видом тестирования хватает, но не стоит спешить списывать их со счетов. Существуют способы и решения, позволяющие минимизировать упомянутые проблемы. Предположим стабильность теста составляет 90%, тогда в среднем 1 запуск из 10 будет давать ложноотрицательный результат. Данный результат не выглядит таким уж радужным. Качество такого сигнала будет довольно низким, и в какой-то момент команда может просто перестать обращать на это внимание или вовсе отключить его. На помощь опять может прийти формула [1]. Если тест падает в 10% случаев и эти события не связаны между собой - можно перезапускать его после первого падения и учитывать результат 2 запусков. Если для нас достаточно чтобы тест прошел хотя бы 1 раз, то в таком случае мы увидим Как видно, UI тесты - довольно мощный инструмент, однако пользоваться им следует очень аккуратно. За время работы с ними, накопился следующий список замечаний:
Подытожим, тесты пользовательского интерфейса:
Надеюсь, удалось остаться до конца объективным и раскрыть как положительные, так и отрицательные моменты тестов, относящихся к верхнему уровню пирамиды. Остались еще моменты и наблюдения, относящиеся UI тестам, которые планирую раскрыть в последующих статьях про интеграционные и контрактные тесты. |