Запуск тестов Django в Bamboo

В этой статье я хочу рассказать про наш опыт организации запуска тестов после коммитов в Bamboo.

Начнем с того зачем писать тесты и автоматически их запускать.

Тесты мы пишем для того, чтобы быть уверенными, что новые изменения ничего не поломали.

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

Чтобы защититься от коммитов, тихо ломающих проект, тесты должны запускаться после пуша в репозиторий с помощью continuous integration(CI). Таким образом, когда кто-то что-то запушит, CI прогонит тесты и всем сообщит о результатах и проблемах, если таковые возникли.

В своей работе мы используем Bamboo. Так сложилось исторически. Мы выбрали JIRA, Confluence, Bitbucket Server. Это всё продукты Atlassian. Для удобной интеграции в качестве CI был также выбран продукт этой компании – Bamboo.

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

 

Итак, тема статьи – как запускать тесты Django-проекта в Bamboo и сделать так, чтобы CI отреагировал на результаты тестов.

Для этого внести некоторые изменения в проект и сделать ряд настроек плане выполнения сборки в Bamboo.

Вот эти шаги:

  1. С целью передать информацию о результатах выполнения тестов в Bamboo, который читает их с помощью JUnit Parser, в проект нужно добавить python модуль, который может выдать XML-отчет о результатах тестов в виде .xml файлов.
    Название этого модуля: unittest-xml-reporting
    Добавить его следует в requirements.txt
  2. Нам потребуется скрипт, который будет создавать виртуальное окружение, прогонять тесты и писать их результаты в .xml файл
    Его листинг я приведу ниже. В нашем случае он будет лежать в корне проекта и название его будет tests_ci.sh
  3. В план сборки (build plan) в Bamboo надо иметь три пункта: Checkout from repository, Run command, JUnit parser.
    Первый пункт создается автоматом и он нам выгрузит код проекта из репы.
    Второй пункт это запуск /bin/bash с именем скрипта для запуска тестов ./tests_ci.sh
    В процессе работы скрипта tests_ci.sh родятся отчеты с названиями файлов вида TEST-*.xml
    Третий пункт, JUnit Parser нужен для того, чтобы считать результаты тестов из файлов. Для этого в настройках этого пункта надо в поле с файлами отчетов написать шаблон TEST-*.xml. Если файлов по такому формату найдено не будет, то сборка будет признана проваленной(failed).

С первым и последним пунктом всё просто, если вы знаете как ставить python-модули и знаете что такое Build plan в Bamboo. Ни то ни другое в этой статье я раскрывать не буду, так как предполагаю, что если вопрос запусков тестов в Bamboo у вас возник, то как-то план сборки вы составить уже смогли. Возможно, я позже напишу статью про настройку Bamboo от начала и до конца.

А вот про особенности tests_ci.sh я сейчас расскажу.

Некоторые входные данные, которые специфичны для нашего подхода к сборке и тестам:

  • В качестве СУБД, где будет создаваться тестовая база используется postgresql.
  • Сервер БД тот же самый на котором крутится dev площадка проекта.
  • Используется тот же самый пользователь БД, что и для dev-версии площадки.
  • С помощью стандартного поведения Django при тестировании название тестовой базы формируется добавлением префикса test_ к имени рабочей БД из настроек. Таким образом, если название БД 'someproject', то название тестовой базы будет test_someproject.
  • Для передачи настроек в проект мы используем переменные окружения, а не хардкодим всё в конфигах.
  • Чтобы пользователь БД мог создавать тестовую базу, то ему нужно дать на это права. Делается это с помощью команды ALTER ROLE db_user with CREATEDB;

Чтобы удалять тестовую базу данных пользователь БД должен иметь права супер пользователя или быть владельцем (Owner) базы данных. Так как пользователь сам и создает эту тестовую БД, то он и будет её владельцем, а значит сможет её и удалить. Никаких прав на удаление БД пользователю давать не надо. (Их собственно и существует). Единственное чего я не нашел в postgres, так это способ ограничить пользователя в правах создавать БД именно с таким именем. Похоже, что такого способа нет. В любом случае, текущий вариант полностью устраивает.

 

#!/bin/bash
rm TEST-*.xml
export ENVPATH="/tmp/testenv"
set -e
if ! cmp $ENVPATH/requirements.txt requirements.txt >/dev/null 2>&1;
then
    rm -rf $ENVPATH
    python3.5 -m venv $ENVPATH
    cp requirements.txt $ENVPATH/requirements.txt
    source $ENVPATH/bin/activate
    pip install --upgrade pip
    pip install -r requirements.txt
else
    source $ENVPATH/bin/activate
fi
source configs/env_dev.sh
export
coverage run --source="panel" manage.py test --noinput --testrunner="xmlrunner.extra.djangotestrunner.XMLTestRunner" panel.tests
coverage report --skip-covere

 

Итак, что делает скрипт.
Во-первых, он удаляет старые отчеты о тестировании командой rm TEST-*.xml. Это нужно на тот случай, если в настройках выполнения плана в Bamboo не стоит галочка удалять рабочую папку сборки после его окончания. Я предпочитаю, чтобы папка удалялась, чтобы наследие от старых версий коммитов и попыток сборок не влияли на последующие сборки.

Дальше мы указываем путь по которому будет жить виртуальное окружение проекта.

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

  1. Можно создавать виртуальное окружение внутри рабочей папки сборки, тогда нам не надо переживать за то, что изменятся requiremens.txt и нам надо пересоздавать окружение. То есть каждая сборка создается полностью с чистого листа и казалось бы это супер удобно и всё здорово. Оборотной стороной медали является то, что при каждой сборке создавать виртуальное окружение очень долго. Сама сборка проходит за 18 секунд, а установка всех пакетов – несколько минут.
  2. Можно вынести виртуальное окружение вне рабочей папки сборки и при каждой сборке запускать pip install –r requirements.txt, тогда все нужные зависимости будут доставлены или установлены нужные версии. Это неплохой вариант, ведь теперь все сборки кроме первой (когда надо поставить всё из requirements.txt в первый раз) будут проходить достаточно быстро, так как большая часть зависимостей уже будет установлена и pip install будет сыпать в логи сообщениями об уже удовлетворенных зависимостях. Этот вариант тоже не идеален и не полностью автоматизирован. Причин для печали две: 1. Время на проверку установленных зависимостей всё равно требуется, хоть и в разы меньше, чем при их фактической установке. Лишние 5-10 секунд ожидания окончания новой сборки тоже могут быть мучительными. Но второй пункт страшнее. 2. Если изменения в requirements.txt связаны с удалением пакетов, то легкого способа автоматически отследить зависимости, которые надо удалить попросту нет. Поэтому я предлагаю третий вариант.
  3. Вариант на котором мы остановились предполагает создание виртуального окружения за пределами папки сборки, а также проверки установленных зависимостей. Проверка актуальности зависимостей проводится путем копирования в virtualenv requirements.txt при создании окружения, а при последующих сборках сравнения файлов requirements.txt в окружении и в проекте. В случае нахождения различий – окружение удаляется и создается заново. Таким образом, этот вариант оказался самым эффективным: если ничего не поменялось в зависимостях, то сборка не замедляется ни на мгновение так как окружение актуальное. А вот, если зависимости изменились, то без какого либо ручного вмешательства окружение будет пересоздано.

После того, как с окружением разобрались, запускается скрипт, которые устанавливает переменные окружения, а затем запускаются тесты.

 

Единственное до чего пока не дошли руки, так это до использования данных из отчета по покрытию кода тестами.

На текущий момент coverage и репорты запускаются, но результат выводится лишь в логе сборки.

 

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

 

Если статья оказалась вам полезна – отправьте её коллегам и друзьям, которые еще решили для себя вопрос запусков тестов при сборках в continuous integration!

Опубликовано: 4 ноября 2016 г.

Special cases aren't special enough to break the rules.

PEP20: The Zen of Python