Разработка технологии непрерывного тестирования программного кода при контейнерной виртуализации на примере многопользовательского мультиплатформенного приложения
Описание технологии автоматизированного end-to-end тестирования, пригодного для применения в современных компаниях, разрабатывающих программные продукты. Изучение тестирования клиентского приложения для платформ iOS, Android и Web и его серверной части.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | дипломная работа |
Язык | русский |
Дата добавления | 01.12.2019 |
Размер файла | 6,1 M |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
8 Разработка Android части
8.1 Разработка приложения
Continuous Integration (далее CI) как технология должна оправдывать свою реализацию в проекте. Приложение может функционировать и без модуля CI, и, если речь идет о маленьком проекте, создание такого модуля будет излишним, потому что время и усилия, потраченные на конфигурацию всех его частей будут превышать дальнейшую пользу. Так, если приложение, в которое внедряется модуль CI, имеет низкую сложность и не представляет оснований к предположению его вероятной ненадежности, модуль CI является излишним.
Таким образом, продумывая приложения для дальнейшей интеграции, мы отталкивались не от идеи приложения и ее целесообразности, а от целесообразности внедрения в дальнейшем в это приложение модуля CI.
Стоит заметить, что CI - это процесс. Он состоит из нескольких этапов, и реализация многих этапов в свою очередь зависит от самого приложения.
Нами было выбрано мультиплатформенное веб-приложение для общественных коммуникаций. Поддержка такого приложения определенно несет в себе ряд сложностей, которые можно решить при помощи добавления к проекту модуля CI.
Важным аспектом этого приложения является необходимость совместного доступа. Это механизм, позволяющий пользоваться приложением с разных платформ одновременно.
Продумав все вышеупомянутые моменты, я приступил непосредственно к разработке приложения.
Подготовка к разработке - один из самых важных шагов, позволяющий значительно упростить последующие этапы разработки. Поддержка приложения, продуманного непосредственно во время разработки, может оказаться крайне проблематичной, или вообще невозможной. Переходя к новым модулям приложения, программист может забыть о возможных конфликтах с другими частями приложения в лучшем случае, или столкнуться с необходимостью переписывать уже существующий код в худшем.
Более того, в ходе очень прямолинейной разработки с большой вероятностью будут нарушены принципы программирования (ООП-принципы в нашем случае), что неминуемо приведет к необходимости переписывания кода. Изменения в UI, логике или данных, могут повлечь за собой изменения в других частях приложения. Подобный код становится практически невозможно протестировать.
Таким образом, для начала мне предстояло выбрать архитектурный паттерн, по которому я бы делал приложение. Такой паттерн должен упрощать разработку приложения в перспективе, делать его легко изменяемым и надежно поддерживаемым.
Мной было рассмотрено несколько вариантов: MV-* паттерны, такие как MVP, MVC, MVVM; Паттерн Clean Architecture; Паттерн RIB.
MV-* паттерны предлагают опцию, при которой логика и данные приложения могут быть написаны таким образом, чтобы каждый компонент мог быть протестирован отдельно. Однако по своей сути такие паттерны направлены в первую очередь на реализацию проблем работы с UI. По этой причине использования только MV-* паттернов недостаточно для построения хорошей архитектуры.
Далее, было решено рассмотреть паттерн Clean Architecture. Его цель состоит в том, чтобы разделить проблемы, не давая бизнес-правилам знать ничего о внешних слоях приложения, чтобы их можно было проверить без какой-либо зависимости от сторонних элементов, таких как библиотеки, различные фреймворки или специализированный подход к UI. Стоит отметить, что каждый уровень использует свою собственную модель данных, чтобы можно было достичь этой независимости. Однако минусом этого паттерна является слишком большое усложнение логики и необходимость в маппинге данных, которые в случае приложения для данного проекта является излишним.
RIB -- это не только архитектурный паттерн. Этот кроссплатформенный фреймворк позволяет обеспечивать приложения качественной архитектурой, по правилам паттерна RIB. Вот его отличительные принципы:
Бизнес-логика управляет приложением, а не деревом представлений;
RIB отделяют структуру областей бизнес-логики от иерархий представлений. Это позволяет приложению иметь глубокое дерево бизнес-логики, изолируя его узлы, в то же время поддерживая поверхностную иерархию представлений, облегчая компоновку, анимацию и переходы.
Однако, в этом конкретном случае сложилась уже вышеупомянутая проблема: такой паттерн помогает при разработке приложения силой нескольких человек, а в моем случае является излишним.
В результате было решено, что идеальным вариантом для этого приложения является модуляризация в купе с паттерном MVP.
Первая задача при создании проекта с таким архитектурным паттерном -- это упорядочить пакеты по функциям. Таким образом достигается ряд преимуществ:
Высокая модульность.
Высокая сплоченность компонентов.
Более простая навигация по коду.
Минимизация объема кода.
Инкапсуляция кода.
Более быстрое время сборки.
Возможность повторного использования общей функциональности.
Уменьшение конфликтов (особенно при работе с git-потоками).
Более контролируемые зависимости.
Организация кода / пакета является одним из ключевых факторов хорошей архитектуры: структура пакета это первое, с чем сталкивается программист при просмотре исходного кода.
В результате проект был создан по следующей модульной системе распределения пакетов:
Рис. 4. Модульная система распределения пакетов.
Древо пакетов на изображении слева (cм. рис. 4) -- это один из логических модулей приложения, а именно авторизация пользователя. Он разделен на три области по паттерну MVP. Пакет view_layer отвечает за отображение данных пользователя и оповещение пользователя о состоянии работы приложения. Пакет presenter_layer управляет пакетом отображения, а также отвечает за бизнес-логику модуля, состояние приложения, переносит и берет данные из пакета model_layer.
Древо пакетов на изображении справа - общий модуль приложения. Здесь хранится базовая часть модуля, отвечающего за работу с сервером, Общие для приложения модели для работы с UI, реактивная библиотека по работе с Web-сокетами, база данных, а также часть бизнес логики приложения.
В результате, тщательно продуманная заранее архитектура позволила в дальнейшем перейти к непосредственному написанию тестов и разработке алгоритма непрерывного тестирования.
8.2 Разработка алгоритма непрерывного тестирования кода:
Чтобы разобраться в особенностях разработки модуля CI, с учетом дальнейшей интеграции, необходимо понять процесс CI и его этапы:
Первый этап интеграции, перенос кода из системы контроля версий приложения в CI tool. Этот этап не зависит от сложности приложения и вообще от кода, как такового. Нужно лишь настроить файл config (.yaml), в котором должна содержаться информация о проекте в системе контроля версий и правила, по которым происходит клонирование в CI-tool. Файл конфиг пишется по следующему шаблону:
before_script:
- export ANDROID_HOME="$HOME/Library/Android/sdk"
- bundle install
stages:
- build
- test
- quality_assurance
- deploy
На втором этапе последняя загруженная версия кода берется из основной ветки инструмента контроля версий и проходит через несколько шагов для проверки его правильности. В случае этого проекта список шагов, следующий: запуск модульных тестов, и сборка приложения в случае успеха. Для завершения этого этапа требуется виртуализация контейнеров. Travis CI оказался самым полезным и надежным инструментом. Он предоставляет простой в использовании сервис, который создает и сохраняет приложение с момента последнего коммита.
На предыдущем этапе приложение было скомпилировано и собрано. Результатом этих действий является специфичный для платформы артефакт, который по умолчанию просто хранится в контейнере инструментов CI. Поскольку мы интегрируем версию приложения для Android, мы получаем файл с расширением «.apk», который представляет собой исполняемую программу, которую можно загрузить и установить. Тем не менее, этот файл требует сгенерированного ключа загрузки и хранилища ключей, с которым он должен быть подписан, для загрузки на платформу Play Market. Это можно сделать с помощью системы «build.gradle».
Автоматизация в целом и тестирование в частности, является критически важным аспектом любого процесса и конвейера CI / CD. Целью автоматизации тестирования является выявление известных проблем. Как упоминалось ранее, это исследование сосредоточено в основном на клиент-серверном взаимодействии продукта. Платформой выбора для этого проекта является Appium из-за его интеграции мобильной платформы.
Непрерывное развертывание - это дополнительный шаг, и он не всегда подразумевается, как необходимая часть CI. Однако в случае этого проекта полностью построенное и протестированное приложение будет загружено в «Play Market».
8.3 Написание E2E тестов на платформе Appium:
Другой важной задачею, которую мы хотели разобрать в модуле CI являлось продумывание и разработка надежного механизма тестирования. Чтобы понять, для чего такой механизм необходим, можно представить себе следующую ситуацию: над одним проектом в команде работает множество разработчиков, некоторые из которых имеют более низкий стаж, чем их коллеги, или просто недавно подключились к новой команде. Один из таких программистов может сделать определенную часть приложения (фичу) и сохранить ее в инструменте контроля версий, но не зная особенностей реализации приложения непременно совершит какой-то недочет или ошибку. Однако эта ошибка не пойдет дальше и не будет замечена пользователями, потому что она натолкнется на «стену» в виде непрерывного тестирования в модуле CI.
Стоит заметить, что с такого рода ситуациями сталкиваются и более опытные разработчики.
Для написания такого блока тестирования мною была выбрана платформа Appium, из-за ее интегрированности с мобильной платформой разработки Android Studio, а также возможности написания тестов на любом предпочитаемым языком программирования.
Проконсультировавшись с моим коллегой по разработке приложения iOS было решено продолжить писать тесты на Python, ввиду его простого синтаксиса и распространенности, а также возможности написания общих тестов на обе мобильные платформы. Таким образом все end-to-end тесты в проекте были написаны на Python с использованием библиотеки PyTest.
Сперва мною были написаны тесты для UI, для тех элементов, с которыми взаимодействовал пользователь.
8.4 Запуск системы:
По завершению работы над каждым элементом системы, была проведена проверка на независимую и совместную работу каждого модуля.
Чтобы проверить работоспособность системы, она была введена в непосредственную эксплуатацию, и все операции по сборке проходили исключительно через нее. Это позволило устранить очевидные ошибки, допущенные в начале разработки системы, и улучшить ее, чтобы в будущем система могла быть применена к любым проектам на платформе Android. Были также протестированы все сценарии работы приложения - были написаны тесты, которые всегда определяющие ошибочный результат работы приложения. Система показала правильные результаты, однако сборка не была выложена - последний эта CI - CD, пока не был введен в эксплуатацию, ввиду отсутствия необходимости в выкладке приложения в Play Market.
После описанных выше шагов код конфигурации системы был рассмотрен и приведен в соответствие с текущими требованиями к разработке программного обеспечения. Все повторяющиеся части кода были представлены в отдельных скриптах. Сами сценарии были разбиты на логические компоненты. Таким образом, система была доведена до готовности к распространению.
9 Разработка Web части
9.1 Разработка веб-приложения
Первой задачей в web-части нашего диплома по теме «Разработка технологии непрерывного тестирования программного кода при контейнерной виртуализации на примере многопользовательского мультиплатформенного приложения» являлась разработка полноценного веб-приложения, на примере которого можно было апробировать разработанную систему тестирования. Приложение должно было быть достаточно сложным, чтобы имелась возможность продемонстрировать все ключевые моменты работы системы тестирования.
Для разработки приложения необходимо было выбрать актуальный на рынке язык программирования и соответствующий фреймворк. Это было необходимо, так как предполагается, что описанная далее технология тестирования будет востребована в IT-компаниях. По этой причине, был выбран язык программирования JavaScript и фреймворк React, выпущенный компанией Facebook, как абсолютные лидеры рынка фронтенд-разработки.
Получившееся приложения покрывает все задачи, решаемые в современной веб-разработке, чтобы успешно продемонстрировать функционал системы тестирования, а именно:
Версия приложения для Desktop-браузера;
Версия приложения для мобильного браузера;
Работа с History API и организация «роутинга» внутри приложения;
Логика работы с REST API;
Логика работы с веб-сокетами;
Логика работы c cookies и localstorage;
Управление глобальным состоянием приложения с помощью Redux.
Рис. 5. Пример интерфейса разработанного приложения для браузера
Если говорить конкретнее о подходах, используемых в решении перечисленных выше задач разработки, то использовались следующие архитектурные решения.
9.1.1 Адаптация под мобильные и Desktop-браузеры
Чтобы тестирование было полноценным, необходимо было создать интерфейс, который способен работать на различных платформах и расширениях экранов. Для этого верстка интерфейсов велась в адаптивной манере и использовались такие решения современной веб-разработки, как: @media queries и webkits для CSS-движков браузеров. Те задачи, которые невозможно было решить исключительно языком стилей, решались с помощью языка JavaScript. Например, необходимо было измерять ширину экрана, чтобы не просто менять стили отдельных компонентов на странице, но и заменять целиком шаблон страниц, как это делается на страницах «Настройки».
9.1.2 «Роутинг» и навигация по приложению
В классических веб-приложениях, организующих SSR (Server Side Rendering), взаимодействие с историей браузера и, соответственно, навигацией по веб-приложению осуществлялось за счет history API [9]. Так как в нашем конкретном случае используется SPA-архитектура (Single Page Application), то для навигации используется React Router, реализующий гораздо более современный подход.
9.1.3 Взаимодействие с сервером
Почти все веб-приложения, реализующие более-менее сложную логику, должны работать с сервером. В нашем случае используется два протокола: HTTP(s) и WebSocket. Через первый организована логика авторизации, регистрации, изменения пользовательских данных и получение истории переписки. Через WebSocket-протокол организован поиск и чат. В первом случае используется специализированный пакет для асинхронных запросов Axios; для работы с сокетами используются нативные интерфейсы языка программирования JavaScript. Логика работы с WebSockets организована в виде конечного автомата.
9.1.4 Работа с хранилищами браузера
Для управления ключевыми данными пользователя, такими, как токены авторизации, используется интерфейсы браузера. Они позволяют фиксировать критичную для пользователя информацию в браузерных хранилищах. Из трех известных: indexDB, localstorage и cookies [10-12] - используются последние два, в виду их удобства использования, так как заводить базу данных для данных задач считается излишним.
9.1.5 Глобальное состояние приложения
Наравне с хранилищами браузера, в разработке современных веб-приложений возникает ситуация, когда необходимо обмениваться данными между страницами и компонентами не через API браузеров, описанных выше. Для этих целей существует архитектурные подход, построенный на Flux-принципе. В данном приложении используется его расширенная версия Redux [13], которая позволяет получать доступ к большим структурам данных, лежащим в коде приложения, не перегружая API браузеров.
Также хочется отметить, что наравне с архитектурными подходами и решениями, реализованными в процессе разработке и описанным выше, одним из основных ключевых преимуществ данного приложения является возможность интеграции его кодовой базы с применяемой в тестировании экосистемой. Так как и разработка, и тестирование ведется на едином языке программирования JavaScript, то окружение, формируемое NodeJS (серверным вариантом языка JavaScript), поддерживается повсеместно. Хочется заострить внимание на следующих положительных моментах:
9.1.6 Общий пакетный менеджер
В разработке и тестировании используется общий пакетный менеджер npm (Node Package Manager) [14]. Соответственно, все загружаемые пакеты, используемые в ходе работы, такие, как:
ImmutableJS - библиотека для работы с иммутабельными структурами [15];
Ramda - библиотека для функционального программирования [16];
Moment - библиотека для работы с датами и календарем [17];
Axios - библиотека для HTTP-запросов [18];
Babel - библиотека для «транспиляции» старого синтаксиса JavaScript-кода в современный ES6 [19]
- хранятся в одной директории проекта и одном Git-репозитории соответственно. Данный момент существенное облегчает работу и помогает избежать излишнего управления зависимостями, чего нельзя сказать о проекте, разработанном, например, на базе Django Python.
9.1.7 Облегченная сборка проекта
Сборка проекта происходит с помощью «бандлера» Webpack [20], open-source`ного решения, позволяющего грамотно «минифицировать» и оптимизировать JavaScript-код для работы в браузере. Помимо непосредственной сборки проекта и уменьшения финального кода, данный сборщик оптимизирует загрузку стилей (в разработке использовался препроцессором SCSS, который «транспилируется» в классический CSS) и обновление HTML верстки. Кроме этого, Webpack делит JavaScript, CSS и HTML файлы на куски, «чанки», что позволяет браузеру подгружать новый код по мере работы в приложении, то есть асинхронно. В дополнении к этому имеется функционал для запуска локального сервера с целью повышения удобства разработки и отладки приложения.
В итоге, можно утверждать, что написанный проект отлично подходит для интеграции с разработанной системой тестирования в общем и современной JavaScript-экосистемой, в частности. Дальнейшее использование, выкладка и тестирование приложения, связанное с такими инструментами, как: TravisCI, WebdriverIO, Selenium Server, Docker и Kubernetes - происходит благодаря работе с написанным веб-приложением, собранным с помощью команд npm (Node Package Manager) и по указанной конфигурации для Webpack. О том, как решались следующие задачи, будет рассказано далее.
9.2 Разработка алгоритма непрерывного тестирования кода
Когда речь заходит о непрерывном тестировании, под этим подразумевается несколько комплексных аспектов:
Контейнеризация;
Оркестрация;
E2E-тестирование.
В описании решения данной задачи будет рассказано о первых двух пунктах. О последнем же речь пойдет в части о Selenium Server и WebdriverIO.
9.2.1 Контейнеризация и Docker
Контейнеры стали стандартами в IT-области, особенно когда речь заходит о работе с развертыванием микросервисов. Основная причина их популярности кроется в том, что, используя контейнеры, не обязательно более «поднимать» отдельные виртуальные машины для развертывания сервисов на машинах. Тех же результатов можно достичь, используя меньше ресурсов. Docker [21] - это одно из самых используемых решений на рынке контейнерной виртуализации.
Контейнеры строятся на базе всей операционный системы. Это означает, что они намного более эффективны, чем, например, гипервизоры [22], используемые в виртуальных машинах. Контейнеры располагаются внутри одной Linux-машины и составляют небольшую аккуратную «капсулу» с приложением или сервисом, который необходимо развернуть или использовать. Поэтому с идеально настроенной контейнерной системой можно в разы увеличить количество экземпляров приложений на одной машине и существенно оптимизировать нагрузки.
Другая причина популярности контейнеров заключается в том, что они отлично подходят для развертывания и тестирования, то есть построения CI/CD-систем. Они позволяют разработчикам чаще обновлять код проекта, а затем быстро и эффективно развертывать код.
Docker позволяет разработчикам быстро упаковывать и запускать любое приложение в виде легкого, портативного, самодостаточного контейнера, который может работать практически в любом месте. Таким образом, контейнеры обеспечивают мгновенную переносимость приложения.
Контейнеры достигают этого путем изоляции кода внутри себя. Это облегчает модификацию и обновление программы. Таким образом, контейнеры позволяют компаниям разбиваться внутри себя на небольшие группы, использующие системы, подобные TravisCI, для автоматизации доставки нового программного обеспечения в контейнерах. Это позволяет организациям делать приложения и их развертывание более мобильными, эффективным, распределенным и стандартизированным.
Кроме того, Docker-контейнеры гораздо легче развертывать в облаке или удаленных машинах, ведь его можно использовать отдельно для управления средами разработки.
В частности, для CI / CD Docker позволяет настраивать локальные среды разработки, которые точно соответствуют обычному серверу; запускать несколько сред разработки на одном хосте с уникальным программным обеспечением, операционными системами и конфигурациями; тестовые проекты на новых или разных серверах; и позволять любому работать над одним и тем же проектом с одинаковыми настройками независимо от среды локального хоста. Это позволяет разработчикам запускать наборы тестов, которые жизненно важны для CI / CD, чтобы быстро увидеть, работают ли вновь сделанные изменения правильно.
Интегрируя CI/CD-систему вместе с Docker, IT-компании с настроенным рабочим процессом выкладывают обновления чаще. Более того, в случае падения систем, они восстанавливаются гораздо быстрее, ведь образ контейнера с предыдущей версией проекта остается на Linux-машине.
На практике нашей системы получается, что frontend-часть приложения имеет в корне проектного репозитория конфигурационные скрипт-файл.
Dockerfile, которые построен на базе alpine, легковесного дистрибутива Linux, и NodeJS, который отвечает за сборку приложения.
Рис. 6. Код скрипта сборки проекта
Также с помощью скрипта в файле docker-compose.yaml указываются порты и зависимости, необходимые для корректного запуска системы.
Рис. 7. Указание портов и зависимостей на базе Dockerfile
Помимо конфигурирования образов для сборки проекта, Docker принимает участие и в процессе тестирования, ведь именно в Docker-контейнере подгружаются браузеры и запускается Selenium Server, в котором и проверяется бизнес-логика приложения.
Рис. 8. Dockerfile для запуска Selenium Server
Далее, когда происходит выкладка приложения или когда запуск Selenium Server`ов, с помощью командной строки происходит сначала сборка контейнеров и их запуск в системе оркестрации Kubernetes, о котором пойдет речь дальше.
9.2.2 Оркестрация и Kubernetes
Kubernetes [23] -- это популярная платформа с открытым исходным кодом для оркестрации контейнеров, то есть для управления приложениями, запущенными в контейнерах. Его существование обосновывается тем, что при использовании большого количества контейнеров появляется необходимость в инструменте, который автоматизирует развертывание, управление, масштабирование, создание сетей и доступность сервисов в контейнерах.
Kubernetes стал одним из самых популярных инструментов оркестрации. На практике Kubernetes чаще всего используется с Docker, самой популярной платформой контейнеризации. Однако он также может работать с любой системой контейнеров, которая соответствует стандартам Open Container Initiative (OCI). И поскольку Kubernetes является open-source проектом, то его можно свободно использовать как локально, так и на удаленной машине.
На самом деле, Kubernetes не заменяет Docker, а дополняет его. Тем не менее, Kubernetes действительно заменяет некоторые технологии более высокого уровня, которые появились вместе Docker. Kubernetes значительно сложнее, чем Docker Swarm (другое решение для оркестрации), и требует больше работы для развертывания. Однако в долгосрочной перспективе система предлагает больший функционал и устойчивость. И так как Kubernetes является лидером рынка во всех более-менее сложных IT-компаниях, то необходимость использования Kubernetes в нашем проекте очевидна.
Сама архитектура Kubernetes использует различные концепции и абстракции. Некоторые из них являются вариациями существующих, знакомых понятий из Docker, но другие характерны только для Kubernetes.
9.2.2.1 Kubernetes кластеры
Кластер в Kubernetes относится к группе машин, на которых запущен Kubernetes и контейнерам, которыми он управляет. Кластер Kubernetes должен иметь так называемого мастера, систему, которая управляет и контролирует все другие машины Kubernetes в кластере.
9.2.2.2 Kubernetes Nodes и Pods
Каждый кластер содержит узлы (англ. Nodes). Узлами могут быть физические машины или виртуальные машины. Опять же, идея заключается в абстракции: независимо от того, на каком приложении выполняется приложение, Kubernetes все равно способен обеспечить управление. Kubernetes даже позволяет гарантировать, что определенные контейнеры работают только на виртуальных машинах или только на голом железе. Например, для отладки работы сервиса через Docker и Kubernetes использовался локальный кластер minikube, запущенный в виртуальной машине на рабочем компьютере.
Узлы запускают поды (англ. Pods), самые простые объекты в Kubernetes, которые можно создавать или управлять ими. Каждый под представляет из себя сервис и состоит из одного или нескольких контейнеров. Kubernetes запускает, останавливает и регулирует количество всех подов.
Поды создаются и уничтожаются на узлах по мере необходимости, чтобы соответствовать желаемому состоянию, указанному пользователем. Иными словами, разработчики могут перераспределять мощности системы между сервисами, путем указания количества подов на каждый сервис. Kubernetes, по факту, предоставляет интерфейсы для управления логикой того, как поды должны изменяться в количестве («скейлиться»), включаться, выключаться и переезжать с ноды на ноду.
Рис.9. Скрипт создания пода
Изначально была предложена идея создать поды отдельно (см. рис. 9), загружая в них сконфигурированная Docker-образы c Selenium Server`ами или приложениями как таковыми, однако это было бы неэффективным использование имеющегося функционала. Поэтому было принято решение использовать Kubernetes-сервисы для автоматизированного управления контейнерами.
9.2.2.3 Kubernetes Services
Поскольку поды то живут, то умирают по различным причинам, появилась необходимость в управлении жизненным циклом запущенных программ. Для этих задач Kubernetes предоставляет абстракцию, называемую сервис (service).
Сервисы в Kubernetes регулируют, как поды могут изменяться, включаться, перезагружаться и переезжать с ноды на ноду. К счастью, сервисы делают это возможным. Например, типичным кейсом в нашем проекте являлось обеспечения доступа к Selenium Servers, запущенных в подах в Kubernetes. Нам было необходимо «выставить» IP-адреса подов на машине, на которой они были развернуты, чтобы система тестирования, при отправке запросов через Webdriver Protocol, могла до них достучаться. Это было реализовано с помощью сервисов NodePort и Deployment, открывающих порты для конкретного пода, указывающий количество CPU и ОЗУ для каждого пода в кластере и регулирующее количеством подов.
Рис. 10. Сервис для управления репликами и нагрузкой подов
9.2.2.5 Итог по Kubernetes
Важно помнить, что сам Docker не заменяет Kubernetes. Поскольку Kubernetes, вводя новые абстракции и концепции, управляет состоянием приложений, репликацией, балансировкой нагрузок и выделением аппаратных ресурсов, то одна из основных обязанностей, которую Kubernetes снимает с разработчиков, -- это работа по поддержанию работоспособности запущенных сервисов. В итоге, вместе с Docker данные инструменты позволили построить удобную и эффективную CI-системы для развертывания web-приложения и E2E-тестирования на базе Selenium Server.
9.3 Selenium Server и WebdriverIO
Selenium Server -- это бесплатный функциональный инструмент тестирования с открытым исходным кодом, используемый для тестирования веб-приложений в нескольких браузерах и на нескольких операционных системах. Используется, в первую очередь, для функционального и регрессионного тестирования.
Сам Selenium Server -- это не просто инструмент, а комбинация двух основных компонентов. Selenium следующих частей:
Selenium WebDriver Protocol;
Selenium Router.
9.3.1 Selenium WebDriver
WebDriver -- это интерфейс удаленного управления, который осуществляет контроль над user-agent [24]. Он является независимым от платформы и языка программирования протоколом для удаленного управления веб-браузером внешними программами.
WebDriver предоставляет набор интерфейсов для обнаружения и управления DOM-элементами на веб-страницах и для управления поведением user-agent`а. Он предназначен, прежде всего, для того, чтобы позволить разработчикам писать автоматизированные E2E-тесты.
Каждый браузер обладает своим собственным протоколом взаимодействия такими, как:
Chrome Driver;
Gecko Driver (Firefox);
IE Driver;
Opera Driver;
Safari Driver.
- которые нужно указывать отдельно для взаимодействия с конкретным браузером. Настройка конфигураций браузеров происходит с помощью тестового фреймворка. Так как веб-приложение существует в экосистеме языка JavaScript, то в качестве тестового фреймворка был выбран WebdriverIO [25], который является переносом классического Webdriver на NodeJS.
WebdriverIO является отличным современным инструментом для E2E-тестирования. Он позволяет:
Добавлять создавать новые, более сложные команды, а API существующих команд гораздо проще для понимания и работы;
Поддерживать Appium и позволяет запускать тесты на мобильных устройствах;
Реализовывать все команды протокола Webdriver и обеспечивает полезную интеграцию с другими инструментами тестирования (отчетами, роутерами).
9.3.2 Selenium Router
Для обеспечения параллельного запуска тестов необходим роутер Selenium запросов. Каждый метод, вызванный через WebdriverIO, по факту, отправляет HTTP-запрос к браузеру, на который он соответствующе реагирует.
Существует несколько готовых решений:
Selenoid;
Selenium Grid;
Gridrouter.
Однако все они страдают большими проблемами при высоких нагрузках: теряются сессии, не доходят запросы, перегружается система, на которой поднятые Selenium сервера, не работают в Docker и Kubernetes. По этой причине было принято написать свое собственное решение по собственной логике (см. рисунок 11).
Рис. 11. Диаграмма логики работы Selenium Router
По факту, были сконфигурированы отдельные Docker-image, содержащие в себе установленные Google Chrome и Firefox браузеры вместе с соответствующими для них Chrome Driver и Gecko Driver (см. рисунок 8). Данные Docker контейнеры были выгружены в Kubernetes кластер и их адреса на хост-машине были выставлены через NodePort для внешнего взаимодействия. Далее, данные адреса были сохранены в MongoDB, из которой каждый тест в момент инициализации получал для себя адрес со свободным Selenium Server`ом. В итоге, получались рабочая система, которая сразу показала свою жизнеспособность, а именно:
Предсказуемое поведение;
Отказоустойчивость на уровне БД;
Масштабируемость;
Портативность;
Поддержка большого количества нод.
9.4 Запуск системы
Запуск системы инициализируется обновлением кода в любой из веток в системе Git и отправке изменений на GitHub. Логика pipeline формируется скриптом в файле travis.yaml, который каждая из компаний сможет редактировать под свои нужды (см. рисунок 12).
Рис.12. Логика pipeline для запуска системы
Таким образом, формируется Docker-образ в системе TravisCI, содержащий в себе весь код проекта. Далее, запускаются тесты с помощью npm-скрипта.
Рис. 13. Запуск скрипта с тестами
Далее, после прохождения тестов сформированный Docker-образ развертывается в удаленном Kubernetes Cluster.
Созданная система успешно отрабатывает во всех случаях. Selenium Router, разработанный под задачи end-to-end тестирования web-приложения, работает бесперебойно за счет логики, построенной на принципе асинхронной очереди. Система гибка и может быть интегрирована в любой процесс разработки, сокращая при этом трудозатраты на сопровождение и управление процессом тестирования и развертывания приложения.
10 Разработка Backend части
Для разработки бэкенд компонента изначально был выбран язык программирования python, и web фреймворка django, в скором времени было принято решение пересмотреть язык, на котором будет разработано приложение в связи с тем, что python не отвечает основным требованиям а именно: в языке не реализован функционал позволяющий полноценно использовать многопоточность, кроме того время исполнения операций дольше чем во многих других.
После проведённого анализа был выбран язык GoLang так как он обладает самым передовым набором инструментов для параллельного исполнения кода, Go-рутины (потоки в GoLang) по сути являются rgeen-threads, что позволяет свободно использовать большое их количество без затрат процессорного времени на переключение контекста. Кроме того, этот язык продвигается корпорацией Google, что свидетельствует о его качестве, и о том, что язык продолжит развиваться и совершенствоваться. Разработка приложения производилась в несколько этапов.
10.1 Проектирование
На этом этапе была спроектирована архитектура приложения, оно было разделено на слои.
Роутер: данный слой отвечает за роутинг всех входящих запросов на конкретные эндпоинты;
Middleware - Слой в котором производится предобработка входящих данных, их анализ и валидация;
API слой - Слой производящий мапинг входящих данных и передачу их на более нижние уровни, а также за обработку результатов функций нижних уровней и формировании http ответа на клиент;
Services- слой, реализующий всю бизнес логику проекта;
Store - слой, отвечающий за хранение всех необходимых данных.
Взаимодействие производится только между слоями, но не между сущностями одного слоя, что придает системе четкость.
Взаимодействие между слоями:
middleware: передает информацию в API слой;
API: получает информацию от Middlewares, передает на сервисный слой, затем проанализировав результаты возвращает их на Клиент-компонент приложения;
Services: получает информацию от API, передает информацию слою Store и различным сторонним сервисам, и обрабатывает ответ от них, передавая результаты анализа его на слой API;
Store: получает информацию от сервисного слоя, выполняет запрос к базе данных и возвращает их на слой Services.
Кроме того, на этом этапе была выбрана база данных MySQL, решение было принято в связи с популярностью данной бд и доступностью документации к ней, так же MySQL обладает рядом инструментов отсутствующих в аналогах (PostGreSQL, SQLite, MsSQL), таких как хинты и кроссплатформенность. После этого необходимо было разработать интерфейс общения между клиентом и сервером, так как SOAP является специфичным а XML устарел было принято решение использовать для передачи информации JSON, за его читабельность, ёмкость, и удобство в использовании (поддерживается на всех языках на которых написаны компоненты приложения) .
Рис. 14. Архитектура backend компонента приложения
10.2 Разработка
После проектирования была проведена разработка в соответствии с принципами разработки SOLID (single responsibility, open-closed, Liskov substitution, interface segregation и dependency inversion). Согласно первому принципу каждая сущность в проекте должна иметь своё элементарное предназначение, например запись в файл, произведение сетевого взаимодействия и тд.
Соблюдение принципа придаёт проекту ясность и позволяет изменят конкретный функционал не затрагивая остальной. Второй принцип гласит что все сущности должны быть закрыты для изменения, но открыты для расширения, разработка в соответствии с этим принципом гарантирует обратную совместимость и гибкость проекта.
Принцип подстановки Барбары Лисков заключается в том, что все сущности могут быть заменены своими наследниками без потери функциональности, его соблюдение гарантирует оправданное и каноничное использование наследия. Суть принципа разделения интерфейса состоит в том, чтобы использовать больше специальных сущностей вместо одной универсальной, что позволит придать коду читаемость, а также способствует соблюдению первого принципа.
Последний принцип больше относится к архитектуре, согласно ему абстракции, не должны зависеть от деталей, а детали должны зависеть от абстракций, если проект разработан в соответствии с данным принципом он является гибким и для разработчиков не составит труда быстро перевести проект на другую базу данных, использовать другой сторонний сервис и заменить любые зависимости.
Разработка велась в среде GoLand так как данный продукт является наиболее развитой на сегодняшний день IDE для разработки на GoLang, обладает большим количеством полезных инструментов таких как пакет навигации по коду, функционал линтера, встроенный дебагер GoLang, пакет подсказок по документации языка, большое количество сторонних плагинов готовых к интеграции, инструменты для настройки экосистемы GoLang, включая аналог пакетного менеджера и многое другое. В совокупности эти инструменты позволяют значительно сократить время разработки продукта.
10.3 Развёртка
Для развёртывания был выбран сервер, предоставленный компании amazon, но с ним возникло множество проблем: главной из них стала проблема частых сбоев с доступом к серверу с территории РФ, проблема решилась после неоднократной смены IP адреса.
Кроме того, единственная компания, предоставляющая инструмент для самостоятельного получения валидных TLS сертификатов, включила большинство стандартных доменных имен, соответствующих amazon E2C серверам, предоставляемым клиентам для размещения и развёртывания приложений в черный список, решить эту проблему удалось только зарегистрировав собственное доменное имя.
После решения вышеупомянутых проблем на сервере была настроена необходимая экосистема, доставлен исполняемый файл backend компонента приложения и произведен его запуск
10.4 Разработка алгоритма непрерывного тестирования кода
После того как первоначальная версия приложения была реализована стала возможной задача разработки алгоритма тестирования. Непрерывное тестирование необходимо при разработке любого проекта так как если данный процесс (тестирование) не автоматизирован, то велик шанс что при доработке проекта (внедрении нового функционала, изменении/исправлении уже существующего) велик шанс не заметить что изменения затронули работоспособность первоначальной системы, автоматизация также позволяет в разы сократить время разработчиков и чем больше проект тем больше времени будет сэкономлено, так как у разработчиков исчезнет необходимость проводить вручную полный цикл тестирования для выявления нарушений в работе системы.
Первым делом был проведен анализ области и выбраны инструменты с помощью которых тестирование будет производится, как показал анализ наиболее подходящим по требованиям к среде в которой будет производится тестирование является виртуальный контейнер Docker, так как благодаря большому и постоянно расширяющемуся репозиторию Docker Hub есть возможность автоматизировать процесс загрузки образа, содержащего необходимую экосистему.
По сути, Docker контейнер является облегченным аналогом виртуальной машины, и обладает удобным “слоистым” принципом работы с данными: изменения в базовый образ вносятся “слоями”, которые могут быть впоследствии различными образами сконфигурированы. Для написания тестов были использованы утилиты языка GoLang, представляющие возможность встраивать тесты в проект с учетом его архитектурных особенностей.
Для того чтоб финальный алгоритм был удобным в использовании он должен обладать главным необходимым качеством: он не должен отнимать время у разработчика и отвлекать его от непосредственной работы (написании кода). Поэтому было принято решение инициализировать запуск тестов основываясь на взаимодействии разработчиков с их VCS (система контроля версий, их используют 90% разработчиков при работе над проектами, ярчайшим и наиболее популярным примером считается GIT VCS), в таком случае это не будет требовать от пользователя каких либо лишних действий при разработке и не будет его отвлекать.
Пользователи необходимо лишь единожды сконфигурировать систему (какие тесты при каких событиях запускать) и затем вести разработку как раньше. После срабатывания триггера (инициализации запуска тестов) необходимо загрузить актуальный код на сторонний сервер, скомпилировать его в соответствии с тестовой конфигурацией и установить на подходящий Docker контейнер, затем провести тесты и в случае неудачи сообщить об этом разработчику.
В случае же удачного прохождения тестов актуальный код необходимо перекомпилировать, используя конфигурацию площадки, на которой впоследствии будет происходить развёртывание. Затем необходимо записать новый слой поверх исходного Docker образа, доставить образ на площадку и развернуть его там, после чего настроить ОС площадки(пробросить соответствующие порты до портов контейнера), в случае любых ошибок нужно информировать о них (ошибках) разработчика, так же как и в случае с успешным выполнением сценария.
Рис. 15. Алгоритм действий системы.
10.5 Написание тестов
Для кода исходного приложения были написаны всевозможные тесты, для серверной части приложения в клиент-серверной модели существуют следующие виды тестов:
Юнит тесты-тесты задача которых проверить работоспособность отдельных базовых функций и методов сущностей, подобными тестами необходимо обкладывать 70-100% соответствующих функций они помогают выявлять банальные ошибки такие как перепутанные имена переменных, использование неправильных математических операторов и базовых операторов языка.
Object тесты-с помощью них выявляются ошибки в работоспособности той или иной сущности проекта, причем если сущность содержит в себе поля также являющиеся сущностями то во время тестирования сущности верхнего уровня их имплементации необходимо заменить тестовыми имплементациями, разработанными так, что их работоспособность будет стопроцентной и не повлияет на результат тестирования сущности полем которой они являются. Для эффективного тестирования область покрытия сущностей проекта подобными тестами должна быть свыше 90%.
API тесты предназначены для тестирования пользовательских сценариев, в режиме подобного тестирования необходимо заменить тестовыми имплементациями лишь те сущности проекта работоспособность которых не зависит непосредственно от разработчика (интеграторы сторонних сервисов, сущности, отвечающие за обращение к базам данных и тд.). Данная группа тестов затрагивает несколько слоёв приложения и выявляет ошибка в их взаимодействии, если зона покрытия проекта API-тестами меньше 90% результаты тестирования нельзя считать актуальными.
Интеграционные тесты, особенность этих тестов в том что они проверяют интеграцию чего бы то ни было в проект, поэтому важно писать их с учетом всевозможных отказов объектов и способов их интеграции (ошибка на стороннем сервере, отказ сетевого взаимодействия, падение движка базы данных), тесты помогают своевременно выявить неработоспособность интегрируемых сервисов и отключить связанный с ними функционал приложения. Если не обложить тестами 100% сущностей, отвечающих за интеграцию то возникает большая вероятность потери части функционала системы и введение ее пользователей в заблуждение.
End-To-End тесты- тесты имитирующие пользовательские запросы к приложению, во время их исполнения все абстракции должны быть имплементированы сущностями, используемыми при полноценной работе приложения, эта группа тестов предназначена для финальной проверки приложения в целом, помогает вовремя выявить как ошибки в самом приложении так и в интерфейсе общения на содной и сторон(клиент-сервер).
Так как приложение написано на GoLang для написания тестов был использован фреймворк “Go test”.
10.6 Запуск системы
После реализации приложения на примере которого будет проверена система тестирования, тестов и самой системы, она была успешно развернута и проверена.
Перед развертыванием системы она была протестирована на различных приложениях, написанных на golang, среди которых были как самодостаточные приложения, так и компоненты различных систем, и имеющих самые различные предназначения, от блокчейн технологий до стриминга аудио и медиа контента. Код был приведен к общепринятому стилю и подвергнут рефакторингу в соответствии со всеми стандартами.
11 Результат работы
В результате была создана автоматизированная система, значительно сократившая время на настройке системы тестирования. Система включает в себя end-to-end тестирование клиентов, а также тестирование серверной части. Это сможет дать будущим пользователям системы, которыми могут быть как маленькие компании, таки и крупны, возможность сосредоточиться на разработке приложения и кодирования тестов.
Готовый CI-продукт представляет из себя полностью функциональную удаленную систему, заточенную на регулярную проверку приложения, тестирование его работоспособности и полного процесса развертывания продукта, который начинается сразу после фиксации последних изменений в ветках в Git. Система готова к использованию и легко адаптируется в соответствии с потребностями текущего проекта, что решает проблему трудоемкой настройки и, безусловно, будет оценена благодаря ее удобству.
Она позволяет сделать процесс интеграции намного надежнее и сэкономить время, затрачиваемое на тестирование приложения. Контейнерная виртуализация делает систему независимой от операционных систем и заставляет ее работать с большей скоростью.
В итоге, конечный продукт способен оптимизировать современный процесс разработки программного обеспечения, который станет неотъемлемой частью любого комплексного многоплатформенного продукта, то есть сервера и клиентов, Android, iOS и web-приложение. Система сэкономит ценные ресурсы для современных ИТ-компаний такие, как: рабочее время разработчиков и расходы на специалистов в области тестирования и разработки.
Кроме того, система предоставляет возможность автоматического развертывания приложения в App Store и Google Play через настроенную систему непрерывной доставки, работающую как на локальных машинах, так и на удаленных облачных сервисах; и автоматическое развертывание веб-сайта на облачных мощностях.
В свете вышесказанного, современный и, в конечном итоге, разработанный подход внешнего тестирования применим почти повсеместно, особенно когда речь идет о смешанном тестировании разных сервисов и клиентов. Результаты проекта демонстрируют, насколько удобным и прозрачным может быть комплексное тестирование сложного приложения и насколько полезными, и легко реализуемыми могут быть результаты для компаний.
Терминологический словарь
Интеграционное тестирование -- определенный шаг тестирования программного обеспечения, при котором отдельные программные модули объединяются и тестируются в группе. Данное тестирование происходит после модульного тестирования и предшествует системному тестированию. Интеграционное тестирование использует модули в качестве входных данных, над которыми было проведено модульное тестирование, объединяет их в более крупные множества, выполняет тесты, определённые в плане тестирования для этих множеств, и представляет их в качестве выходных данных и входных для последующего системного тестирования.
TravisCI -- распределённый веб-сервис для сборки и тестирования программного обеспечения, использующий GitHub в качестве хостинга исходного кода. Программная составляющая сервиса также располагается на GitHub'е, однако разработчики не рекомендуют использовать её в закрытых проектах.
yaml -- (обертка над json) «дружественный» формат сериализации данных, концептуально близкий к языкам разметки, но ориентированный на удобство ввода-вывода типичных структур данных многих языков программирования. Расшифровывается как «Yet Another Markup Language» -- «Ещё один язык разметки», позже -- «YAML Ain't Markup Language» -- «YAML -- не язык разметки».
Appium -- это инструмент автоматизации с открытым исходным кодом для запуска скриптов и тестирования собственных приложений, мобильных веб-приложений и гибридных приложений на Android или iOS с помощью веб-драйвера.
Selenium -- это инструмент для автоматизации действий веб-браузера. В большинстве случаев используется для тестирования Web-приложений, но этим не ограничивается.
Docker -- программное обеспечение для автоматизации развёртывания и управления приложениями в средах с поддержкой контейнеризации. Позволяет «упаковать» приложение со всем его окружением и зависимостями в контейнер, который может быть перенесён на любую Linux-систему с поддержкой cgroups в ядре, а также предоставляет среду по управлению контейнерами. Изначально использовал возможности LXC, с 2015 года применял собственную библиотеку, абстрагирующую виртуализационные возможности ядра Linux -- libcontainer.
Kubernetes -- открытое программное обеспечение для автоматизации развёртывания, масштабирования и управления контейнеризированными приложениями. Поддерживает основные технологии контейнеризации, включая Docker, rkt, также возможна поддержка технологий аппаратной виртуализации.
Сервисно ориентированная архитектура -- модульный подход к разработке программного обеспечения, основанный на использовании распределённых, слабо связанных заменяемых компонентов, оснащённых стандартизированными интерфейсами для взаимодействия по стандартизированным протоколам. Программные комплексы, разработанные в соответствии с сервис-ориентированной архитектурой, обычно реализуются как набор веб-служб, взаимодействующих по протоколу SOAP, но существуют и другие реализации. Интерфейсы компонентов в сервис-ориентированной архитектуре инкапсулируют детали реализации (операционную систему, платформу, язык программирования) от остальных компонентов, таким образом обеспечивая комбинирование и многократное использование компонентов для построения сложных распределённых программных комплексов, обеспечивая независимость от используемых платформ и инструментов разработки, способствуя масштабируемости и управляемости создаваемых систем.
Список использованных источников
Jenkins vs. Travis CI vs. GitLab CI. URL: https://stackshare.io/stackups/gitlab-ci-vs-jenkins-vs-travis-ci (дата обращения 9 мая 2019).
Shaporda O. Continuous Integration. CircleCI vs Travis CI vs Jenkins. URL: https://hackernoon.com/continuous-integration-circleci-vs-travis-ci-vs-jenkins-41a1c2bd95f5 (дата обращения 18 апреля 2019).
WebdriverIO vs?Nightwatch.js. URL: https://www.slant.co/versus/9647/9648/~webdriverio_vs_nightwatch-js (дата обращения 26 апреля 2019).
Serkovskiy P. Как выполнять много UI-тестов параллельно, используя Selenium Grid? URL: https://habr.com/ru/company/avito/blog/352208/ (дата обращения 17 апреля 2019).
Подобные документы
Архитектура и история создания операционной системы Android. Язык программирования Java. Выбор средства для реализации Android приложения. Программная реализация Android приложения. Проведение тестирования разработанного программного обеспечения.
курсовая работа [167,8 K], добавлен 18.01.2017История развития и виды тестирования программного обеспечения. Инсталляционное, регрессионное, конфигурационное, интеграционное, локализационное, модульное тестирование. Методы сокращения трудоемкости модульного тестирования разрабатываемого приложения.
курсовая работа [309,5 K], добавлен 16.12.2015Назначение и архитектура экспертных систем, их применение в сфере образования. Экспертные системы тестирования, принципы их функционирования. Инструментальные средства создания приложения и разработка программы тестирования. Описание программы, листинг.
дипломная работа [706,4 K], добавлен 07.05.2012Проектирование базы данных, информационной подсистемы PLC-Tester, модуля тестирования и web-приложения. Разработка логической структуры программного продукта и общие требования к техническому обеспечению. Запуск программы и описание тестовых прогонов.
дипломная работа [3,2 M], добавлен 30.06.2011Методика и основные этапы разработки системы тестирования для оценки уровня знаний студентов с применением технологии "Клиент-сервер". Проектирование клиентской, серверной части данной системы тестирования, порядок составления финальных отчетов.
дипломная работа [587,6 K], добавлен 08.11.2010Разработка приложения, которое будет выполнять функции показа точного времени и точной даты. Определение дополнительных функций разработанного приложения. Рассмотрение основных этапов создания программного продукта. Результаты тестирования приложения.
курсовая работа [2,2 M], добавлен 14.04.2019Технология создания многопоточных приложений в современных системах программирования с использованием языка C# в Visual Studio.NET. Разработка алгоритма и структуры программы. Описание и особенности тестирования приложения с разным количеством потоков.
курсовая работа [773,0 K], добавлен 14.03.2013Разработка модели системы тестирования пользователей с применением технологии "клиент-сервер". Требования к программному изделию и документации. SADT диаграмма системы тестирования до и после автоматизации. Настройка SQL-сервера и установка программы.
курсовая работа [1,5 M], добавлен 22.01.2013Изучение различных видов тестирования программного обеспечения. Выявление в программной системе скрытых дефектов до того, как она будет сдана заказчику. Тестирование методом черного ящика. Требования, предъявляемые к процессу тестирования больших систем.
курсовая работа [3,0 M], добавлен 19.11.2009Неразрешимость проблемы тестирования программного обеспечения. Виды и уровни тестирования. Стратегии восходящего и нисходящего тестирования. Методы "белого" и "черного" ящика. Автоматизированное и ручное тестирование. Разработка через тестирование.
курсовая работа [112,2 K], добавлен 22.03.2015