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

Описание технологии автоматизированного end-to-end тестирования, пригодного для применения в современных компаниях, разрабатывающих программные продукты. Изучение тестирования клиентского приложения для платформ iOS, Android и Web и его серверной части.

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 01.12.2019
Размер файла 6,1 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

Аннотация

Данная работа описывает технологию автоматизированного end-to-end тестирования, пригодного для применения в современных компаниях, разрабатывающих программные продукты. Разработанный подход позволяет тестировать клиентские приложения для платформ iOS, Android и Web, а также серверную часть. В процессе разработки был применен подход, построенный на базе контейнерной виртуализации и оркестрации, применяющий такие CI/CD инструменты, как: Kubernetes, Docker, TravisCI, Selenium, Appium. Для разработки использовались языки и фреймворки: Java, Swift, Javascript, React, Webdriverio, Golang, а также базы данных: MySQL и MongoDB. В качестве облачных решений были выбраны Amazon Webservices и Google Kubernetes Engine.

Abstract

This paper describes the technology of automated end-to-end testing, suitable for use in modern companies that develop software products. The developed approach allows testing client applications for iOS, Android and Web platforms, as well as the server side. In the development process, an approach based on container virtualization and orchestration was applied using CI / CD tools such as: Kubernetes, Docker, TravisCI, Selenium, Appium. The following languages and frameworks were used for development: Java, Swift, Javascript, React, Webdriverio, Golang, and also databases: MySQL and MongoDB. Amazon Webservices and Google Kubernetes Engine were chosen as cloud solutions.

Оглавление

Введение

1 Актуальность разработки

2 Цели и задачи

2.1 Обоснованность

2.2 Практическая значимость работы

2.3 Постановка задачи

3 Внедрение результатов работы

4 Личный вклад

5 Апробация результатов

6 Анализ предметной области

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

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

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

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

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

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

7.2 Разработка алгоритма непрерывного тестирования кода

7.3 Написание E2E тестов на базе Appium

7.4 Запуск системы

8 Разработка Android части

8.1 Разработка приложения

8.2 Разработка алгоритма непрерывного тестирования кода:

8.3 Написание E2E тестов на платформе Appium:

8.4 Запуск системы:

9 Разработка Web части

9.1 Разработка веб-приложения

9.2 Разработка алгоритма непрерывного тестирования кода

9.3 Selenium Server и WebdriverIO

9.4 Запуск системы

10 Разработка Backend части

10.1 Проектирование

10.2 Разработка

10.3 Развёртка

10.4 Разработка алгоритма непрерывного тестирования кода

10.5 Написание тестов

10.6 Запуск системы

11 Результат работы

Терминологический словарь

Список использованных источников

Введение

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

1 Актуальность разработки

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

Отсутствие сбоев при развертывании приложений;

Облегчение способа изменения версии приложений: как обновление, так и возврат на прошлые версии;

Реализация мультиплатформенного тестирования;

Снижение временных и денежных издержек на ручное тестирование;

Снижение временных и денежных издержек на поддержку сетевой и серверной инфраструктуры.

Таким образом, современная концепция разработки программного обеспечения должна позволять спланировать процесс так, чтобы разработчики перестали вручную тестировать, создавать и развертывать программное обеспечение или нанимать для этих задач отдельных сотрудников. Большинство систем, не связанных с непосредственным кодированием, должны быть автоматизированы. Вот почему системы CI/CD, включающие, как и развертывание, так и тестирование, как никогда актуальны.

2 Цели и задачи

Целью разработки является автоматизированная и применяемая на IT-предприятиях система интеграции версий мультиплатформенного приложения: его тестирования и развертывания.

2.1 Обоснованность

тестирование приложение клиентский android

Обоснованность результатов исследований существующих инструментов, выводов и практических решений, полученных в дипломной работе, обеспечиваются применением ее компонентов в различных российских IT-компаниях. Так, например, компания OneTwoTrip, где работали Заблудовский Глеб, использовала схожий подход для контенейризации и оркестрации в своей инфраструктуре. Компания Ozon.ru добавила в свой Kubernetes-кластер Selenium Server`а, таким образом наладив end-to-end тестирование. Компания Finch, в которой работал Фесенко Георгий, применила его решение и настроила себе процесс развертывания под платформу iOS. Практические результаты, сформулированные в дипломной работе, обоснованы проведенными исследованиями и результатами разработки и могут служить руководством при решении реальных задач индустрии.

2.2 Практическая значимость работы

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

2.3 Постановка задачи

Первоначальная задача исследования - описать и показать на примере мультиплатформенного приложения наиболее актуальный подход в области CI/CD, основанный на опыте некоторых ведущих российских ИТ-компаний и на практических исследованиях. Эффективный и простой подход можно представить, ответив на следующие вопросы:

Почему так важно стремиться к Continuous Integration и end-to-end тестированию?

Каким образом должно быть организовано развертывание нового кода?

Где и как должны запускаться приложения и бэкенд?

Как правильно запускать тесты?

Как организовать процесс написания тестов на соответствующих языках программирования?

Как правильно и понятно измерять производительность?

Какие шаги необходимы для интеграции изобретенной системы в бизнес-процессы?

Как разработанная технология влияет на процесс разработки?

Вместе с этим, данную работу можно разбить на две основные подзадачи.

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

Вторая подзадача носит более исследовательский характер и состоит в том, чтобы изобрести подходящий алгоритм для использования такой системы, поскольку ее применение предполагается в современном IT-мире.

В связи со сложностью проекта было решено распределить задачи между участниками проекта по платформам:

Web

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

Привязка технологии тестирования к одной платформе, как для веб, так и для iOS и Android;

Конфигурация удаленной Linux машины и создания кластера с контейнерами, с запущенными внутри Selenium серверами;

Написание end-to-end тестов для веб-приложения;

Создание continuous integration для веб-приложения.

iOS

Написание iOS приложения со сложной внутренней логикой, включающей такие технологии, как: веб-сокеты, поиск по геолокации, авторизация пользователя;

Написание end-to-end тестов для iOS приложения;

Разработка системы прогона тестов с использованием технологии контейнерной виртуализации на базе open-source платформы Appium.

Android

Написание Android приложения со сложной внутренней логикой, включающей такие технологии, как: веб-сокеты, поиск по геолокации, авторизация пользователя;

Написание end-to-end тестов для приложения Android;

Разработка системы прогонки тестов с использованием технологии контейнерной виртуализации на базе open-source платформы Appium.

Backend

Проектирование и разработка современного API, обслуживающего мобильные и веб-версии;

Создание платформы для тестирования API;

Написание unit-тестов, разработка и реализация позитивных и негативных сценарных тестов для сервера.

DevOps

Конфигурация удаленной Linux машины для веб-сервера приложения;

Разработка общего принципа быстрого continuous integration и распространение практики на другие платформы в команде;

Интеграция репортеров и цифровых отчетов для формирования наглядного результата тестирования на всех платформах;

Управление нагрузками на обоих Linux-машинах, веб-сервере и кластере с контейнерами Selenium Server'a.

3 Внедрение результатов работы

Результаты дипломной работы применены полностью или частично в двух IT-компаниях: Ozon.ru и Finch.

Ozon.ru полностью автоматизировал end-to-end тестирование интерфейсов за период с осени 2018 года по весну 2019 года. Изначально, существовало несколько проблем, которые замедляли обновление программного продукта:

Написание тестов велось на языке Python, а не на языке разработки JavaScript;

Отсутствовала инфраструктура для запуска end-to-end тестов;

Тестирование не было включено в pipeline развертываемого приложения.

В итоге, компания исправила эти недочеты, начав разрабатывать тесты на языке разработки с помощью JavaScript-фреймворка WebdriverIO и запуская тестирование на Selenium Servers в общем Kubernetes-кластере. К весне все команды написали свой набор тестов, составляющих примерно 40 штук на команду, которые запускаются при каждом релизе, в каждой feature­-ветке, а также осуществляются ночные прогоны. Таким образом, в одном из крупнейших E-commerce компаний России было автоматизировано end-to-end тестирование интерфейсов.

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

4 Личный вклад

Обязанности были распределены по платформам:

Оленев Александр Юрьевич (группа БИВ153) отвечал за разработку web-клиента, настройку CI для web-приложения, конфигурирование Selenium Server и проектирование Selenium Router для параллельного запуска тестов;

Фесенко Георгий Евгеньевич (группа БИВ154) отвечал за разработку клиента под iOS, настройку CI для iOS-приложения, конфигурирование Appium для автоматизированного тестирования мобильного приложения;

Кузнецов Виталий Владимирович (группа БИВ154) отвечал за разработку клиента под Android, настройку CI для Android-приложения, конфигурирование Appium для автоматизированного тестирования мобильного приложения;

Заблудовский Глеб Владимирович (группа БИВ154) отвечал за разработку серверной части, включая проектирование баз данных, настройку end-to-end тестирования с помощью Docker и настройку CI для приложения на языке GoLang

Калугин Антон Сергеевич (группа БИВ154) выполнял роль DevOps-инженера, отвечал за настройку облачных решений на базе сервисов Amazon Web Services и Google Kubernetes Engine, за управление баз данных MySQL и MongoDB, используемых в ходе разработки.

5 Апробация результатов

Результаты, полученные в ходе работы над дипломным проектом, представлялись и обсуждались в двух IT-компаниях:

Оленев Александр Юрьевич выступал с докладом на тему «Эффективное end-to-end тестирование frontend» на встрече разработчиков интерфейсов компании Ozon.ru (Москва, 2018), где рассказывал про то, как организовать тестирование с помощью фреймворка WebdriverIO и Selenium Server.

Фесенко Георгий Евгеньевич выступал с докладом на тему «CI/CD для разработки под iOS» в компании Finch (Москва, 2018), где рассказывал о том, как правильно выстроить процесс развертывание мобильного приложения под iOS на базе TravisCI.

6 Анализ предметной области

Разработка технологий непрерывного тестирования и интеграции сама по себе не является чем-то новым для мира программного обеспечения. История развития компьютерной науки и продуктовой разработки насчитывает несколько попыток создать надежный способ развертывания и тестирования приложений для различных платформ. Зачастую использовался комплексный подход, заключающийся в применении нескольких инструментов, которые вкупе могли бы значительно улучшать проверку качества ПО. Однако получающиеся системы не являлись надежными по причинам нестабильности инфраструктуры или сложности интеграции с кодовой базой проекта.

Чаще всего предпринималась попытка сделать единую точку входа для end-to-end тестирования мобильных приложений, веб-приложений и серверного кода, однако эта идея изначально была нежизнеспособна. Причина в том, что технологии под каждую из платформ изначально строились на разных подходах и языках программирования и далее, что закономерно, развивались порознь. Поэтому системы, построенные на базе так называемой «фермы тестирования», оказывались слишком хрупкими, ведь гораздо труднее настроить инфраструктуру для бесперебойного тестирования, чем просто написать тест-кейсы.

По этой причине, в данной работе было принято решение сделать единой точкой входа не обертку в виде микросервиса, а облачную систему интеграции на базе 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 запрос и прислать соответствующий этому запросу ответ. Для возможности реализации подобного уровня абстракции был добавлен класс-конфигуратор, который и передается в метод отправки запроса. Это позволило сделать полностью переиспользуемый класс для работы с сетью. Ниже приведен набор классов и интерфейсов, составляющих переиспользуемый основной слой:

protocol IRequest {

var urlRequest: URLRequest? { get }

}

protocol IParser {

associatedtype Model

func parse(data: Data) -> Model?

}

struct RequestConfig<Parser> where Parser: IParser {

let request: IRequest

let parser: Parser

}

protocol IRequestSender {

func send<Parser>(config: RequestConfig<Parser>, completionHandler: @escaping (Result<Parser.Model>) -> Void)

},

где IRequest - интерфейс структуры, хранящей в себе информацию о запросе;

IParser - дженерик интерфейс для структур, отвечающих за распознавание приходящих данных, где Model - тип или структура данных, получаемая после преобразования;

RequestConfig - необходимый уровень абстракции, позволяющий совместить в одном месте структуры запроса и ответа;

IRequestSender - интерфейс для класса, занимающегося отправкой сетевого запроса и получения ответа. Именно класс, реализующий этот интерфейс, комбинирует в себе все описанные выше интерфейсы и структуры.

Реализация классов будет приведена в приложении.

Слой сервисов стоит следующим после основного слоя в иерархии и содержит в себе бизнес-логику конкретного приложения, под которое этот слой был написан. Именно в этом слое находится логика работы с геолокацией и веб-сокетами - основной слой для этих технологий писать не имеет смысла, так как он предоставляется в библиотеках, которые импортируются перед написанием кода класса сервиса. Так, для работы с геолокацией была выбрана библиотека, написанная компанией Apple - CoreLocation, а для работы с сокетами - библиотека с открытым исходным кодом - Starscream. Сервис, при необходимости, может содержать в себе экземпляр нужного ему класса из основного слоя, инжектируемый, как правило, посредствам композиции.

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

После внесения вышеобозначенных изменений, программа стала удовлетворять принципам проектирования и стала пригодна для расширения, изменений и, самое главное, для написания unit- и end-to-end- тестов. Появилась возможность создавать mock-объекты и закрывать ими сервисы, тем самым тестировать бизнес-логику без привязки к программному интерфейсу бэкенд-приложения. Это значительно упрощает написание unit-тестов и позволяет покрыть тестами все возможные участки кода, что делает возможным использовать приложение для указанных в техническом задании задач. Основной слой приложения представляет из себя оттестированную кодовую базу, пригодную для интеграции в любое приложение, так как осуществлено на уровне интерфейсов, а не на уровне реализации, что позволяет подставлять в созданные классы любые объекты, реализующие необходимые интерфейсы.

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

Так же в программе повсеместно используются принципы SOLID[]:

Single responsibility principle (Принцип единой ответственности) - каждый класс, будь то сервис или компонент основного слоя, или же элемент отображения, имеет всего одну ответственность, что позволяет повысить его переиспользуемость;

Open/Closed principle (Принцип открытости/закрытости) - классы, модули, функции должны быть открыты для расширения, но закрыты для изменения;

Liskov substitution principle (Принцип подстановки Лисков) - функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа, не зная об этом;

Interface segregation principle (Принцип разделения интерфейсов) - лучше создавать много отдельных интерфейсов, чем один универсальный.

Dependency inversion principle (Принцип инверсии зависимостей) - модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те и другие должны зависеть от абстракций.

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

В программе особое внимание уделено отсутствию антипаттернов проектирования. Таким образом в коде не используется паттерн Singleton (невозможно оттестировать класс, созданный в согласии с этим паттерном), нет жесткой связности (при соблюдении принципа инверсии зависимостей данной проблемы не возникает). Код написан с расчетом на последующее тестирование. В программе отсутствует преждевременная оптимизация - все архитектурные решения были приняты из явной необходимости. Все переменные названы в соответствии с их назначением, что дает возможность не всегда погружаться в код, а понимать происходящие из именования. Повторение кода сведено к минимуму, но с оглядкой на отсутствие преждевременной оптимизации. Существует, так называемое, правило трех - согласно которому если код повторился один раз, то проблемы нет, но если его приходится переписывать в третьем месте, то лучше инкапсулировать логику. Это имеет смысл, так как инкапсулировать логику для двух вызовов не всегда оправданно и является нарушением принципа YAGNI (You ain't gonna need it - тебе это не понадобится).

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

7.2 Разработка алгоритма непрерывного тестирования кода

В ходе работы над алгоритмом непрерывного тестирования он претерпел большое количество изменений, доработок и улучшений. Изначально, в качестве системы контроля версий был выбран Gitlab, а для запуска тестов и доставки сборок - Gitlab CI. Было решено запускать тесты на локальной машине, которая в рамках терминологии Gitlab называется Runner'ом и конфигурируется перед добавлением в репозиторий. Был написан файл конфигурации .yml и все необходимые скрипты, о которых речь пойдет ниже. Выбор в сторону такого стока технологий был сделан из-за удобства и замкнутости системы - репозиторий готов к работе с системой непрерывного тестирования сразу и это позволяет сосредоточиться на других аспектах разработки.

В вышеописанном подходе существуют недостатки, главные из которых - привязанность к определенной системе контроля версий (не всегда Gitlab удобнее аналогов), локальная машина может сломаться или затормозить, иногда нужно больше одной машины для параллельной прогонки тестов или выгрузки сборок (у одной машины может не хватить мощностей). Для решения вышеописанных проблем было решено перейти на другую систему, а именно - Travis CI. Так же было решено поменять систему контроля версий на Github для возможности сделать исходный код проекта открытым (Github лучше подходит для подобных целей, чем любая другая платформа). Travis CI был интегрирован с репозиторием проекта на Github и сконфигурирован для выкладки сборок.

Для исполнения скриптов и написания логики был выбран Fastlane. Fastlane - это обертка над скриптами IDE XCode, которая включает в себя базовые проверки и решает определенное количество низкоуровневых проблем. Эта система была выбрана, чтобы избежать уже решенных ранее сообществом проблем и перейти к дальнейшей разработке, а также из-за возможности посмотреть открытый исходный код, выложенный на Github. Так же Fastlane предоставляет скрипты для взаимодействия с API iTunes Connect, что значительно упрощает процедуру электронной подписи программного кода посредствам сертификата и профиля приложения.

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

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

В случае с Testflight ситуация проще - после прохождения автоматического тестирования члены команды в iTunes Connect (их может быть до 25 человек и они должны быть распределены по ролям) получают доступ к сборке, а для выкладывания на внешнее тестирования придется отправить приложения на рассмотрение и дождаться пока сборку пройдет ручное тестирование силами Apple. Данная процедура в первый раз занимает до трех дней, но при загрузке новых сборок проходит не дольше минуты. Разработанное приложение уже прошло подобное тестирование и доступно к скачиванию по ссылке.

Было решено настроить процесс выкладки таким образом, что при заливании кода в ветку develop происходила выкладка на платформу Fabric, а при заливании в master - в Testflight. Такое решение было принято исходя из идеологического назначения соответственно проименованных веток. Именование develop используется для ветки, в которой ведется разработка, иными словами - в нее довольно часто заливается код с доработками из других веток. Такие сборки нет смысла выкладывать для широкого круга тестировщиков, так как в них будет серьезное количество багов, которые могут быть пойманы без их участия. В master же выкладываются сборки, которые предполагается выложить в магазин приложений, то есть выкладка происходит уже значительно реже, чем в develop, и требует более тщательной проверки, что предполагает распространение сборки на внешнее тестирование.

Файл конфигурации на этот момент разработки выглядел следующим образом:

language: swift

osx_image: xcode10

xcode_project: Point.xcworkspace

xcode_scheme: Point

xcode_destination: platform=iOS Simulator, OS=11.4, name=iPhone X

before_install:

- sudo gem install fastlane -NV

- sudo gem install cocoapods --pre

script:

- sh build.sh

branches:

only:

- develop

- release

- master

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

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

После идет самое значимое поле - script. В нем размещается необходимый для исполнения код. В ходе разработки было принято решение перенести код этого раздела в отдельный файл - build.sh и разместить его в директории проекта. Полный код данного файла доступен в Приложении.

В конце файла конфигурации указываются ветки, при пуше в которые должен запускаться код из раздела scripts.

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

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

final class URLSessionMock: URLSession {

typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void

var data: Data?

var error: Error?

override func dataTask(with request: URLRequest,

completionHandler: @escaping CompletionHandler) -> URLSessionDataTask {

let data = self.data

let error = self.error

return URLSessionDataTaskMock {

completionHandler(data, nil, error)

}

}

}

final class URLSessionDataTaskMock: URLSessionDataTask {

private let closure: () -> Void

init(closure: @escaping () -> Void) {

self.closure = closure

}

override func resume() {

closure()

}

}

Для удобства был обозначен новый тип данных CompletionHandler, затем объявлены два поля - поле данных (data) и поле ошибки (error), которые нужно заполнять при использовании класса, в зависимости от желаемого поведения (именно из-за этого они не объявлены как приватные). Затем указанные объекты при создании объекта задачи передаются в объект-заглушку сессии, которая переопределяет метод, отвечающий за обращение к сети (resume) и передает в ассинхронном ответе заранее известные данные. Подобная практика часто используется в unit-тестировании для тестирования внутренней логики приложения, без зависимости от работы сторонних сервисов.

В случае же с UI-тестами (они же end-to-end) ситуация куда более неоднозначная. Подобные тесты запускают приложение и нажимают на конкретные элементы интерфейса, иными словами - они имитируют взаимодействие человека с интерфейсом. Подобное тестирование может занимать очень много времени и требует оптимизации. Было принято решение использовать технологию контейнерной виртуализации, а как инструмент взять Appium. При использовании подобной связки существует возможность распараллелить запуск тестов в контейнерах и значительно ускорить тем самым процесс тестирования программного кода. Помимо всего прочего Appium дает возможность не использовать исходный код для запуска UI тестов, что существенно быстрее, нежели родное тестирование посредствам среды разработки XCode.

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

Рис. 3. Обобщенная схема работы с Appium

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

- ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

- brew upgrade node

- sudo npm install -g appium --unsafe-perm=true --allow-root

- sudo pip install Appium-Python-Client

- pip install -U pytest

- brew upgrade carthage

В первой строке производится установка homebrew, затем обновляется npm, затем устанавливается Appium, затем производится установка библиотек python, необходимых для запуска тестов, и в конце производится обновление менеджера зависимостей carthage, необходимого для корректного запуска тестов.

Таким образом конечный алгоритм тестирования состоит из iOS приложения, написанного в соответствии со всеми требованиями, предъявляемыми к современному программному обеспечению и, в том числе, в соответствии с требованием о тестируемости кода, что позволяет написать нативные unit-тесты, использовать технологию контейнерной виртуализации для запуска end-to-end тестов посредствам платформы Appium и запускать это на машинах, предоставляемых платформой Travis CI, которая также связана с Github репозиторием проекта и позволяет наладить запуск любых скриптов базируясь на пушах в указанные ветки.

7.3 Написание E2E тестов на базе Appium

Одно из преимуществ Appium'a над системой тестирования, предоставляемой интегрированной средой разработки XCode, заключается в том, что у пользователя данной платформы есть возможность выбрать язык программирования, на котором будут написаны UI-тесты.

Выбор стоял между многими языками программирования (Java, Objective-C, JavaScript (Node), PHP, Python, Ruby, C#, Clojure, Perl) и было решено остановиться на Python, ввиду его простого синтаксиса и распространенности. Помимо этого, python довольно просто изучить с нуля, что является довольно важным критерием для языка тестирования, ведь впоследствии написание UI-тестов можно частично переложить на специалистов по тестированию, и им будет намного проще выучить python, чем Java. Таким образом все end-to-end тесты в проекте были написаны на Python с использованием библиотеки PyTest.

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

Затем тестами покрывался экран настроек. Основное, что там нужно было проверить - правильность момента сохранения данных. Если пользователь нажимает на кнопку «назад» - данные сохраняться не должны и при последующем переходе к экрану настроек пользователь должен увидеть неизмененную информацию. Если пользователь нажимает кнопку «ок» - данные должны сохраниться и при последующем переходе на экран настроек пользователь должен увидеть измененные данные.

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

Пример теста выглядит следующим образом:

def testPhoneTextField(self):

phoneTextField = self.driver.find_element_by_accessibility_id('phoneTextField')

phoneTextField.send_keys("+79999999999")

sleep(1)

self.assertEqual(phoneTextField.get_attribute("value"), "+7 999 999-99-99")

Сначала находится элемент на экране с соответствующим id доступа, установленным заранее. Затем в поле записывается информация с помощью метода send_keys, после чего программа на всякий случай ждет непродолжительное время и затем берет значение из поля. Ожидаемый результат - данные отформатировались.

Одной из возможностей платформы Appium является написание одних и тех же сценариев на разные платформы (iOS и Android). Мы, совместно с Android разработчиком, изначально использовали подобную практику, но впоследствии отказались от этого. Дизайн под разные платформы может отличаться и, таким образом, наличие одинаковых тестов под разные платформы может в один момент все усложнить - подобная система не была бы расширяемой, что противоречит принципам проектирования.

7.4 Запуск системы

Перед запуском системы в работу было проведено ее повсеместное тестирование на отказоустойчивость и выдачу результатов, соответствующих действительности - сборка приложения должна выкладываться только при всех пройденных тестах. Так как в системе участвует значительное количество сторонних компонентов, таких как Travis CI, Github, Appium, их работоспособность по отдельности проверяется силами тех разработчиков, которые их предоставляют, и, в связи с тем, что обозначенные компоненты уже давно используются сообществом и имеют исключительно положительную репутацию, то было решено перейти к тестированию связки компонентов, считая, что по-отдельности они всегда работоспособны. Такое допущение было сделано исходя из вероятностных соображений, так как все обозначенные выше компоненты имеют свой набор всех возможных тестов и разрабатываются корпорациями, шанс того, что они откажут несущественен.

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

После описанных выше действий код конфигурации системы был просмотрен и приведен в соответствие с современными требованиями к разработке программного обеспечения. Все повторяющиеся участки кода были вынесены в отдельные скрипты. Сами же скрипты были разбиты на логические составляющие. Так, все взаимодействия с системой сборки XCode и API iTunes Connect были вынесены в файл Fastfile, написанный на языке программирования Ruby (используется для написания скриптов Fastlane). Логика, обозначающая поведение скриптов, в зависимости от ветки, была вынесена из файла конфигурации Travis CI в отдельный файл build.sh (написан на языке bash) для большей читаемости. Таким образом система была приведена в готовый к распространению вид.

Все последние сборки, выложенные на платформу testflight с ветки master или же на платформу fabric с ветки develop, прошли UI- и Unit-тестирование с помощью Appium и встроенной системы для Unit-тестов среды разработки XCode.


Подобные документы

  • Архитектура и история создания операционной системы 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

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.