Две недели назад закончилась Школа Автоматизации Процессов Разработки (ШАПР) в санкт-петербургском Яндексе.
Забегая вперёд, скажу, что преподавателям удалось главное: за неполных три месяца они «испортили» наше восприятие процесса разработки, как и обещали в начале Школы. Ручное тестирование и сборка начали жутко раздражать, вызывая мысли вроде «а ведь сейчас, вместо выполнения рутинных операций, я бы мог писать красивый код ...».
Подробнее о том, как им это удалось, можно прочитать здесь и ещё немного здесь.
В практической части обучения мы реализовали web-блог, на базе перечисленных по ссылке выше технологий.
Меня, как android-разработчика, заинтересовала возможность применения этих технологий и инструментов для организации Continuous Integration для Android.
Вдохновлённый докладом Алексея Коровянского на MBLTDev, и, особенно, результатами Google Test Automation Conference 2014 касательно тестирования Android-приложений, я реализовал упрощенный аналог нашего учебного проекта под другую платформу, перенеся туда всё что смог и успел. На этом предисловие заканчивается и начинается собственно описание автоматизации.
1. Автоматизированное тестирование
Мы говорим автоматизация, подразумеваем — тесты; говорим тесты, подразумеваем — автоматизация.
Тестирование, зачастую, одна из самых ресурсоёмких частей процесса разработки. Текст на кнопке можно поменять за пару минут (включая время запуска IDE), собрать проект ещё за пару минут — а потом несколько часов проверять, не поехала ли вёрстка во всех местах использования этой кнопки при всех контрольных разрешениях экрана. Если этот процесс не автоматизирован, конечно.
К счастью, в декабре 2014 Google всё-таки порадовал android-разработчиков, включив фреймворк Espresso в Android Support Repository. Ура! Наконец-то у нас есть мощный, достаточно стабильный инструмент написания системных тестов, который поддерживает разработчик операционной системы (помимо всего прочего, это даёт надежду, что тесты не посыпятся с выходом новой версии Android).
Про Espresso на Хабре уже писали, так что я ограничусь замечанием, что в текущей версии он позволяет синхронизировать операции в тестах не только с UI Thread и AsyncTasks, но и с произвольными фоновыми операциями (http-запросами, к примеру). Пример реализации системных и интеграционных тестов можно посмотреть в моём проекте на github, ссылка в конце статьи.
2. Автоматизированная сборка
В наш просвещённый век, наверное, нет необходимости описывать преимущества, которые даёт использование систем сборки.
В ШАПР мы использовали Maven, но под Android Studio с её поддержкой Gradle «из коробки», что-то другое использовать просто неудобно.
Для запуска тестов использовались задачи
- connectedAndroidTest — из списка типовых gradle tasks
- spoon — появляется при установке соответствующего плагина. Очень полезная разработка команды Square под руководством небезызвестного Jake Wharton. Позволяет запускать тесты на нескольких подключенных android-устройствах одновременно, делать с них скриншоты, и многое другое
3. Автоматизированный запуск тестов
Если вы каким-то чудом не используете Maven или Gradle, то дальше можете не читать — ни один сервер непрерывной интеграции с вашим проектом дружить не будет.
А если используете, то после нескольких запусков нескольких десятков/сотен тестов к проекту у вас возникает большое желание, чтобы это всё происходило самостоятельно, и, желательно, как можно дальше от вашего процессора :-). Здесь нам на помощь приходит Jenkins (TeamCity, Atlassian Bamboo, нужное подчеркнуть). Я использовал Jenkins, которому нас и учили в ШАПР. Про Jenkins и даже непосредственно его отношения с Android-приложениями на Хабре написано тоже немало.
В нашем случае, помимо плагинов «по умолчанию», использовались
- GitHub plugin, GIT plugin, GitHub API Plugin — для получения кода и перехвата событий из github
- Gradle plugin — для запуска gradle tasks
- Android Emulator Plugin — для запуска приложения на эмуляторе (потом отказался от этой идеи в пользу spoon)
- HTML Publisher plugin — для публикации описания результатов выполнения тестов произвольного формата на странице задачи
- JUnit Plugin — для публикации результатов выполнения unit-тестов
Вообще запуск тестов на реальных устройствах и виртуальных образах genymotion показал лучшие по быстродействию, стабильности и реалистичности результаты, чем стандартный эмулятор Android от Google, так что от его использования в тестировании я на данный момент отказался.
4. Пару слов про разделение окружений
Речь идёт об использовании разных, к примеру, баз данных для testing и production. Для этого в код необходимо транслировать различные настройки. В модельном проекте это реализовано с помощью gradle build flavors, переменные вынесены в отдельный файл с константами, который плагин подставляет в нужную сборку.
Итак, как выглядит частично автоматизированный процесс разработки модельного android-приложения на данный момент:
1. Написанный код уходит на github.
2. Jenkins ловит hook с github и запускает сборку на всех android-устройствах, которые запущены/подключены к серверу/ноду
3. По результатам сборки Jenkins отписывается на github/посылает письмо разработчику/зажигает красную или зелёную лампочку над дверью (нужное подчеркнуть)
4. В случае успешной сборки установочный файл заливается на тестовые устройства, а тестировщикам-ручникам уходит оповещение, что можно приступать к работе (если это необходимо)
5. Возможно даже автоматически опубликовать собранный проект в GooglePlay с помощью Google Play Android Publisher Plugin
TODO: к сожалению ещё не успел поднять SonarQube, хотя в ШАПР мы успели наглядно оценить полезность и важность инструментов анализа кода. Да, и code coverage в тестовом проекте пока не считается. Не успел использовать механизм dependency injection, который очень удобен для написания хорошо тестируемого кода — под Android для этого есть замечательная библиотека Dagger. В общем, есть ещё чем заняться.
Обещанная ссылка на модельный проект (блог с возможностью добавления/удаления постов/комментариев) и дюжину тестов к нему. Не судите код слишком строго, ведь чтобы его имело смысл тестировать, он должен иногда ломаться, верно? ;-)
В проекте использовались библиотеки:
- robospice
- retrofit и okhttp
- androidannotations
- cupboard
- ну и, конечно, espresso
Пользуясь случаем, хочу искренне поблагодарить наших инструкторов в ШАПР и Яндекс в целом.
Вы в очередной раз сделали мир немножко лучше. Спасибо! :-)
Автор: mairos