Введение
В процессе разработки, создавая новый функционал, всё чаще широкими мазками стал задевать старый код чем разрушал логику его работы. Это заставило всё-таки написать юнит и интеграционные тесты для старого кода и автоматизировать их запуск, т.к. гонять руками все тесты как-то грустно. Как раз вспомнилось недавнее руководство по CI Django в Jenkins и довольно старое по Webtest в Django. В итоге была совершена попытка поднять Дженкинса, но он как-то на моей убунте не взлетел и я грешным делом вспомнил про TeamCity. «Раз уж пишу в PyCharm и нашёл к нему подход, то, наверно, и TeamCity осилю, ведь конторка-то одна!» — подумалось мне… В общем-то я оказался прав, и, пока мне позволяет карма, решил подарить вам ультраполезный (и мегаподробный), в отличие от моего предыдущего, мануал :)
Итого: кому требуется руководство по поднятию интеграционного сервера TeamCity, и тестирование в нём Django проектов c тестами nose и webtest в виртуальном окружении python с автоматическим его (окружения) обновлением — добро пожаловать под кат.
Осторожно! Для работы TeamCity требуется (согласно документации) sun/oracle версия JVM…
Теория
Виртуальное окружение python с помощью virtualenv
Система на моём ПК для разработки удовлетворяет всем требованиям моего проекта, но это далеко не так на сервере для тестирования (даже python там 2.6, а мне в итоге понадобился 2.7), потому пришлось озаботиться виртуализацией окружения, чтобы спокойно ставить то, что нужно мне, кроме того это избавляет от необходимости обладать властью
root
'а или sudo
er'а.Для создания виртуального окружения нужно установить virtualenv или скачать и извлечь из архива
virtualenv.py
. Мы будем использовать второй вариант, т.к. это совсем избаляет нас от необходимости пользоваться власть суперпользователя. Да, в системе всё-таки должен быть установлен python.python virtualenv.py -p python2.7 .env
Здесь
-p python2.7
задаёт необходимую версию python (тонкость в том, что нужные версии питона должны быть установлены в самой системе, но не быть при этом «питоном по-умолчанию», скрипт не сможет их сам вытянуть и установить), а .env
каталог в котором будет размещено окружение. Далее, чтобы использовать python и модули из окружения нужно вызывать их через .env/bin/python
если же писать это лень — можно выполнитьsource .env/bin/activate
который заменяет переменную окружения
PATH
.Автоматизация через fabric
Кроме самого питона установленного на целевой системе, нам также потребуется модуль fabric. То есть на сервере тестирования придётся выполнить команду
sudo pip install fabric
или sudo easy_install fabric
, при установке потянет pycrypto
которому для сборки нужны сорцы питона из python-dev
, так что сначала нужно поставить его. Fabric позволяет исполнять скрипты на python, которые выполняют действия на локальной, либо удалённой (через SSH) системе. После установки в системе появится приложение fab
, которое ищет файл fabfile.py
в папке вызова и исполняет указанный скрипт.Например:
fab test
выполнит выполнит функцию
test
из файла fabfile.py
.Nose и webtest в django
Теперь немного о самих тестах. У django есть свои юнит-тесты и функционалные тесты (с помощью тестового клиента), но есть в них и недостатки о которых писано не однократно. В общем я стал использовать nose для юнит-тестирования и webtest для функционального. Во введении я давал ссылку на статью про webtest, там неплохо показано как это работает. Нам для тестирования понадобятся следующие модули (потом этот список мы добавим в файл со списком модулей для автоматической установки через
pip -r
):coverage>=3.0
nose
webtest
django-nose
django-webtest
Кроме того
django_nose
нужно добавить в INSTALLED_APPS
файла settings.py
вашего django проекта, либо в том же файле указать TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
.Внимание! Если используется
south
, то django_nose
надо добавлять после него.Собственно, если есть юнит-тесты, уже написанные в джанговской системе тестирования — их можно не трогать, всё будет работать и так. Как писать функциональные тесты через webtest можно прочитать в вышеупомянутой статье.
Запускается тестирование как обычно:
python manage.py test
Эта команда выполнит как nose тесты так и webtest тесты, если названия файлов, классов и функций с тестами имеют в названии слово
"test"
— это бонус от nose
. Кроме того nose
может использовать coverage для рассчёта покрытия кода тестами (функциональные webtest
тесты он не понимает, т.к. тесты работают с кодом не напрямую, а через генерённые страницы). Django-nose
позволяет нам использовать опции запуска nose тестов. Для этого их можно прописать в файле settings.py
проекта. Например:NOSE_ARGS = [
'--nocapture',
'--with-coverage',
'--cover-html',
'--cover-html-dir=%s' % os.path.join(PROJECT_PATH, 'cover', 'unit'),
'--cover-package=django_dir'
]
Это заклинание позволит сгенерировать отчёт о покрытии тестами в HTML формате и поместить его в подкаталог
cover/unit
вашего проекта.Практика
Структура проекта
Мой проект имеет следующую структуру папок:
.
..
project_dir
project_dir/django_dir
project_dir/build
project_dir/build/pipreq.txt
project_dir/fabfile.py
project_dir/virtualenv.py
Где
project_dir
содержит в себе всякие полезные штуки типа каталог с виртуальным окружением, файл фабрики и прочие полезные штуки, а уже в подкаталоге django_dir
располагается собственно сам проект на django. В систему контроля версий (в моём случае это GIT) попадает всё начиная с project_dir
с небольшими исключениями вроде каталога с виртуальным окружением.Выполнение тестов
Собственно наша цель научить TeamCity после обнаружения изменений в репозитории (что он умеет сам) протестировать наш обновлённый django проект (чего он не умеет) и сообщить нам о результатах (тут ему нужно тоже немного помочь).
Дело в том, что для чистоты эксперимента, желательно, чтобы кроме самих тестов проверялось/обновлялось и всё окружение, для этого мы напишем небольшую фабричку которую, после пролучения изменений, и будет запускать TeamCity.
Код
project_dir/fabfile.py
:# -*- coding: utf-8 -*-
from fabric.api import local
def test():
"""
Запуск тестов.
"""
local('python virtualenv.py -p python2.7 .env') # создаём виртуальное окружение
local('./.env/bin/pip install -q -E .env -r build/pipreq.txt') # ставим модули окружения
# перечисленные в build/pipreq.txt
local('./.env/bin/python django_dir/manage.py test --noinput') # запускаем тесты
Обратите внимание, что
virtualenv.py
лежит в корне проекта, чтобы его не требовалось устанавливать в системе.Необходимое содержание
project_dir/build/pipreq.txt
:django
coverage>=3.0
nose
teamcity-nose
webtest
django-nose
django-webtest
...много много ваших модулей которые постоянно добавляются/меняются...
Обратите внимание в файле перечислен ранее не упоминавшийся teamcity-nose, этот модуль и помогает передать TeamCity результаты нашего тестирования. Он не требует никакой настройки и подключения — запускается автоматически когда находит переменную окружения
TEAMCITY_PROJECT_NAME
которую создаёт TeamCity во время работы.Собственно всё готово к автоматическому тестированию. Чтобы его начать достаточно выполнить команду:
fab test
в общем корне проекта.
Установка и настройка TeamCity
Я сильно заморачиваться не стал, чего и вам рекомендую — просто скачал tar.gz вариант со страницы загрузки. Весит всё это счастье неприличные 333МБ. Но в комплекте несёт кроме самого сервера интеграции ещё и Tomcat, что избавит нас от геморроя с настройками серверов и прочей лабудой.
Есть у TeamCity свойство (упомянутое в начале), которое может кому-нибудь не понравиться — он требует для работы сановскую/оракловую версию JVM.
Как получить её на Ubuntu можно почитать к примеру тут.
Собственно после скачивания распаковываем это добро там, где оно будет работать (сами данные по-умолчанию будут храниться в подкаталоге
.BuildServer
домашнего каталога пользователя под которым запущен сервер). Запускается сервер командой bin/runAll.dh start
, убивается соответственно bin/runAll.dh stop
(если находимся в папке с распакованным архивом). Сервер по-умолчанию крутится на 8111
порту, но есть ещё build agent
который стартует на 9090
порту. Первый можно изменить в файле conf/server.xml
, второй в файле buildAgent/conf/buildAgent.dist.properties
(образец) и buildAgent/conf/buildAgent.properties
(конкретный агент).Все остальные настройки (ну про которые я расскажу) будут делаться через веб интерфейс. В общем стартуем teamcity и идём в браузер (http://localhost:8111 если запускался локально). В зависимости от мощности системы вы либо увидите лицензионное соглашение, либо сообщение:
и нужно будет подождать пока оно сменится лицензионным соглашением.
Далее будет предложено создать администраторский аккаунт. После чего мы увидим уже саму систему TeamCity.
Тут наc интересует создание проекта. Там всё просто — название и описание. После мы попадаем на страницу настроек проекта.
На вкладке VCS Roots (VCS — version control system) нужно задать новый репозиторий. Точнее указать откуда система будет брать код для тестирования. Там длинная портянка которую нужно заполнить вашими данными. Все поля подписаны, имеются линки на страницы помощи, так что ничего сверхчеловеческого там нет. Тут же можно проверить работоспособность соединения.
Далее на вкладке General нужно создать новую Buid Configuration — краеугольный камень нашего тестирования.
Build counter — номер с которого начнётся нумерация билдов, а точнее номер билда который будет присвоен следующей сборке. Можете смело ставить
1
, тут просто скриншот не свежий :)Из важых полей на первой странице здесь Artifact paths который указывает системе как найти файлы возникающие в результате сборки/тестирования. В нашем случае это отчёты по покрытию тестами. В моём случае здесь достаточно ввести
django_dir/cover/**/*
. На второй странице выбираем наш ранее заданный репозиторий.Нажав на Add Build Step нужно будет создать первый (и, в нашем случае единственный) шаг сборки (в нашем случае тестирования).
Собственно требуемый набор настроек вы видите на скрине. Сохраняем.
Далее справа в вертикальном меню нужно выбрать Build Triggering и добавить запуск нашей сборки при изменении репозитория:
Ну и напоследок возвращаемся к настройкам самого проекта, идём на вкладку Report Tabs и создаём новую вкладку:
это позволит TemCity отображать наши отчёты в своём интерфейсе.
Вкладка появится тогда, когда в указанном месте будет указанный файл, так что если её нет — проверьте настройки и посмотрите действительно не ли отчёты лежат там где должны.
Вот и всё, остаётся в самом верхнем меню в Projects перейти к нашему проекту и нажать Run у билда, для первого прогона.
Красивости
Все менеджеры любят графики, потому чтоб было, что показать нужно сделать несколько картинок. Увы картинки эти добавляются истинно программерско-админским методом — внесением изменений в XML конфиг. С руководством можно ознакомиться здесь. Чуствуется что в этом месте разработчики изрядно отдохнули (могли бы и формы сделать чесслово). Т.к. очевидного способа узнать один из важнейших параметров
buildTypeId
я не нашёл. Самое простое — посмотреть в адресной строке браузера: localhost:8111/viewType.html?tab=buildTypeStatusDiv&buildTypeId=bt2
. Приведу конфиг двух своих графиков:
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<report-tabs>
<report-tab title="Coverage" basePath="/unit/" buildTypeId="bt2">
<revisionRule name="lastFinished" revision="latest.lastFinished" />
</report-tab>
</report-tabs>
<custom-graphs>
<graph title="Build Duration" defaultFilters="showFailed" seriesTitle="key"> <!-- Беспонтовый график для тестов -->
<valueType key="BuildDuration" title="main test" buildTypeId="bt2"/>
</graph>
<graph title="Test Passes" defaultFilters="showFailed" seriesTitle="key"> <!-- Результаты тестов -->
<valueType key="FailedTestCount" title="fail" buildTypeId="bt2"/>
<valueType key="PassedTestCount" title="pass" buildTypeId="bt2"/>
</graph>
</custom-graphs>
</settings>
Это файл
~/.BuildServer/config/django_dir/plugin-settings.xml
. Изменения в файле подхватываются на лету.Выглядит это как-то так:
А вот так выглядит отчёт по покрытию:
Список сборок:
Подробности по сборке:
Заключение
Прошу прощения за «многобукаф,» но так уж вышло. Хотелось описать доступно. В итоге мы имеем автоматизированное тестирование (модульное и интеграционное), кроме того проверяется ещё и наличие необходимых модулей и вообще «поднимаемость» проекта. Разборки с реагированием TemaCity на различные результаты сборки я оставляю на вашей совести. В этой части есть чему порадоваться — и стандартное мыльное уведомление, и уведомление через jabber, и создание релизного пуша.
Удачной разработки!