Дипломная работа: Разработка технологии непрерывного тестирования программного кода при контейнерной виртуализации на примере многопользовательского мультиплатформенного приложения

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

По этой причине, в данной работе было принято решение сделать единой точкой входа не обертку в виде микросервиса, а облачную систему интеграции на базе Git. В нашем конкретном случае был использован TravisCI. В итоге, вместо написания слишком абстрактной и сложной в поддержке прослойки для запуска тестов, программисты могут формировать логику тестирование конкретных платформ путем написания специальных .yaml-скриптов, а не поддерживать отдельный модуль с раздутым интерфейсом.

Рис. 1. Пример интерфейса TravisCI с запущенным pipeline для iOS-приложения

О том, как была реализована разработка, можно будет прочитать далее в отчете, однако стоит провести конкретный анализ предметной области. Анализ будет состоять из анализа:

1. Систем интеграций;

2. Систем тестирования серверных приложений;

3. Фреймворков тестирования мобильных приложений;

4. Фреймворков и подходов тестирования web-приложений.

6.1 Системы интеграций

Основой для развертывания приложения являются инструменты, представляющие из себя конфигурацию в виде скрипта, лежащего в корне проекта с кодом. В данном скрипте можно вызывать Bash или Shell команды, позволяющие подгружать на runner CI-системы нужные файлы, с которыми можно работать далее в сценарии pipeline`а. Это могут быть (как в нашем случае) сконфигурированные Docker-image`ы с кодом проектов под различные платформы, что, например, позволяет тестировать бизнес-логику клиентов после обновления серверной части. Разные системы интеграции обладают различными качествами [1-2]. Сравнение наиболее популярных решений представлено в таблице 1.

Таблица 1

Сравнение систем интеграций Jenkins, TravisCI, Gitlab

Инте-

грация с Gihub

Бесплат-ный open-

source проект

Улучшенная поддержка мобильных платформ

Инте-

грация с Docker

Много-

язычность

Автомати-

ческое разверты-

вание

Богатая документ-ация

Jenkins

-

+

-

+

-

+

-

TravisCI

+

+

+

+

+

+

+

GitlabCI

-

-

-

+

+

-

+

В итоге, нами был выбран инструмент TravisCI, так как он идеально подходит под нужды развертывания и тестирования многоплатформенного и мультиязыкового проекта. Однако стоит отметить, что самым большим плюсом являются лучшая поддержка мобильных приложений и интегрированность с сервисом удаленного хранения кодовой базы GitHub, который является мировым лидером и используется как в малых, так и в больших компаниях, что отлично подходит под наши задачи.

6.2 Системы тестирования серверных приложений

Самым популярным фреймворком для тестирования серверных приложений на сегодняшний день является PyTest, занявший эту позицию благодаря своей универсальности и простоте в использовании, но, как и все универсальные инструменты он проигрывает узко специализированным платформам тестирования в конкретно взятых случаях, в нашем случае фреймворк go test имеет ряд значительных преимуществ:

Благодаря нативности платформа позволяет использовать сущности, непосредственно используемые в проекте, что гарантирует отсутствие ошибок и затраты на написание дополнительной прослойки интерфейсов нужных лишь для тестирования и бесполезных непосредственно для проекта.

Не имеет огромного количества зависимостей, не используемых для непосредственных целей проекта, что позволяет существенно сэкономить ресурсы и время на загрузку зависимостей и разворачивание дополнительной экосистемы (в случае с python это тяжелая операция по памяти и времени)

Экономит само время тестирования так как взаимодействует непосредственно с сущностями проекта, а не с некоторой прослойкой-интерфейсом, что позволяет ускорить тестирование в десятки раз, причем преимущество по времени растут в геометрической прогрессии относительно количества тестов. Так же экономит время на написание тестов, так как при использовании PyTest для каждой тестируемой сущности необходимо реализовать интерфейс для взаимодействия с платформой тестирования (в подобной ситуации покрыть юнит тестами все элементарные функции не представляется возможным).

Команде не потребуется привлекать стороннего python специалиста и тратить время на интеграцию его в технические тонкости проекта.

6.3 Фреймворки тестирования мобильных приложений

Фреймворки для тестирования существуют практически на всех языках и многие из них уже очень давно поддерживаются и используются для совершенно разных целей. В связи с тем, что тестированию любого кода, будь то мобильное или веб-приложение, предполагает конечный набор действий над кодом, такое обилие фреймворков кажется довольно бессмысленным. Как пример - PyTest (для языка программирования Python), XCTest (Swift), UiAutomator2 (Java/Kotlin). Appium позволяет абстрагироваться от конкретных фреймворков и выбрать любой из них. В рамках продуктовой разработки такая возможность является крайне востребованной, так как появляется возможность отдать задачи по написанию end-to-end тестов специалистам по тестированию, которые не всегда знают нативный язык разработки, но скорее всего умеют писать тесты на каком-либо другом фреймворке.

6.4 Фреймворки и подходы тестирования web-приложений

В корне проблемы end-to-end тестирования интерфейсов web-приложений лежат два фундаментальных вопроса: на каком языке писать тесты и как тесты следует запускать? Правильные ответы на данные вопросы позволят автоматически избавиться от неудобства в разработке системы тестирования и облегчат ее поддержку в будущем.

По нашему мнению, отвечая на первый вопрос, лучше кодировать тесты на языке разработки продукта, если существует такая возможность и инструменты не являются сырыми. Это, в свою очередь, позволяет:

Повысить переиспользуемость кода;

Хранить код с тестами в одном репозитории с кодом проекта;

Облегчить поддержку зависимостей;

Заинтересовать разработчиков в написании end-to-end тестов и сократить, таким образом, издержки на разработку.

Так как современные web-приложения строятся на базе языка JavaScript, то и фреймворки для тестирования стоит выбирать на этом языке. На данный момент мировой рынок предлагает следующие возможности [3]:

Таблица 2

Сравнение JavaScript-фреймворков для end-to-end тестирования

Полная документация

Новый стандарт языка ES6

Поддержка любой assertion-

библиотеки

Синхронная работа с Selenium protocol

Необходимость запускать Selenium Server отдельно

WebdriverIO

+

+

+

+

-

NightwatchJS

-

+

-

-

+

WD.js

-

-

-

-

+

Из таблицы 2 видно, что наиболее развитым биндингом Selenium Protocol`а на NodeJS является фреймворк WebdriverIO. Единственный минус связан с тем, что он не запускает автоматически Selenium Server, однако в нашем случае это не является проблемой, так как мы намеренно запускаем Selenium Server`а внутри Kubernetes кластера.

Вторым вопросом, как уже было сказано, является способ запуска тестов. Под запуском, в первую очередь, подразумевается распараллеливание тестов для быстрого выполнения. Распараллеливание необходимо, так как зачастую end-to-end тестирование выполняется до развертывания приложения, и поэтому желательно, чтобы pipeline не выполнялся слишком долго.

Сегодня существует несколько готовых решений, работающих по следующему принципу, такие, как: Selenium Grid, Grid Router или Selenoid [4].

Рис. 2. Обобщенный принцип работы Selenium Grid

Однако всех их объединяют недостатки, которые не позволяют наладить бесперебойную работу end-to-end тестов:

Непредсказуемость поведения;

Отсутствие поддержки большого количества сессий;

Сложность масштабируемости;

Слабая отказоустойчивость;

Абсолютное отсутствие поддержки создания динамических Node с использованием систем контейнеризации и оркестрации.

По этой причине было принято решение разработать свой собственный Selenium Router, который оказался гораздо легче в поддержке и масштабировании, понятнее в архитектурном плане, стабильнее и предсказуемнее.

7 Разработка iOS части

7.1 Разработка приложения на платформу iOS

Для запуска и полноценного использования системы тестирования было необходимо разработать приложение, обремененное сложной логикой, обусловленной использованием ряда технологий, таких как веб-сокеты, геолокация. Для использования последней было необходимо удостовериться в том, что пользователь приложения предоставил все необходимые разрешение и дал возможность получать его геолокацию в любой момент времени использования устройства.

Архитектурно приложение изначально писалось с использованием паттерна проектирования под названием Модель-Представление-Контроллер, который обозначает ряд возможных связей между тремя обозначенными в названии сущностями. Пользователь всегда взаимодействует с Контроллером, который обрабатывает его нажатия на экран и содержит всю бизнес-логику, выполняющуюся по нажатию. У Контроллера есть Модель, которая отвечает за хранение данных, полученных после исполнения обработки события нажатия. Так же Модель может косвенно обновлять третью сущность паттерна - Представление. Представление отвечает исключительно за отображение информации для пользователя, базируясь на данных, полученных из модели, которая, в свою очередь, постоянно формируется Контроллером. Представление ни в коем случае не должно обрабатывать нажатия на экран, а Котроллер не должен заниматься отображением элементов - иначе смысл паттерна теряется.

Выбор стоял между паттернами MVC (Модель-Представление-Контроллер), MVVM (Модель-Представление-Модель Представление) и VIPER (Представление - Интерактор - Презентер - Сущность - Роутер) [5]. MVC был выбран из-за его наибольшей распространенности в кодовой базе Apple. VIPER нужен в более объемных проектах, а зачастую даже тогда, когда ведется разработка нескольких приложений - появляется возможность переиспользовать VIPER-модули в разных проектах. MVVM обычно используется в связке с парадигмой реактивного программирования, что противоречит идеологии компании Apple.

Как можно заметить из описания - у выбранного паттерна проектирования есть один недочет - не очень понятно, кто занимается запросом данных из сети, из локального хранилища, кто реагирует на изменения геолокации и оповещает сервер. Все это по идеологии выбранного паттерна делает Контроллер. При написании логики работы экранов авторизации проблем с данным паттернов не возникало - в Контроллере было достаточно мало программного кода, и программа оставалась читаемой. Проблемы начались при написании логики для работы с геолокацией, где нужно было на одном экране уместить логику для взаимодействия с сетью, геолокацией и локальным хранилищем - в подобных ситуациях паттерн Модель-Представление-Контроллер перестает выполнять функцию разделения обязанностей полноценно, так как слишком много логики возлагается на одну сущность - Контроллер. Снижается возможность переиспользования фрагментов кода, увеличивается количество дублирующих участков и, помимо перечисленного, код становится трудно воспринимать из-за слишком большого количества строк кода на класс.

Читаемость кода - очень важный элемент современной разработки, которым не стоит пренебрегать. Так же, как и принцип единой ответственности, который нарушается при попытке уместить всю логику в один класс. По это причине после 2-х месяцев разработки было решено дополнить паттерн Модель-Представление-Контроллер еще двумя слоями, следую принципам сервисно-ориентированной архитектуры. В данной парадигме программный код разбивается на три слоя - Представление, Сервисный, Основной.

В основном слое, который находится в самом низу иерархии и предполагает наибольшую переиспользуемость, находится логика, никак не связанная с бизнес-логикой приложения. Основной слой пишется с расчетом на то, что его, при необходимости, можно будет переиспользовать в другом приложении. Именно в нем находится логика отправки сетевых запросов, открытия/закрытия сокетов, работы с локальной базой данных. Интерфейс основного слоя никак не должен отражать приложение, для которого этот слой был написан. Таким образом, сущность, отвечающая за сетевые запросы, обычно называемая NetworkClient, должна уметь принять как аргумент функции любой http/https запрос и прислать соответствующий этому запросу ответ. Для возможности реализации подобного уровня абстракции был добавлен класс-конфигуратор, который и передается в метод отправки запроса. Это позволило сделать полностью переиспользуемый класс для работы с сетью. Ниже приведен набор классов и интерфейсов, составляющих переиспользуемый основной слой: