Внешние инструменты тестирования для Django

Кому-то хватает стандартных инструментов тестирования Django, кому-то нет. Мне стандартного мало и я сделал обзор сторонних инструментов тестирования в Django.

В обзор попали:

  • django-sane-testing
  • django-test-utils
  • django-satprep
  • nosedjango
  • tddspry

Самое забавное, что я опросил уважаемых мною знакомых “джангонавтов”, и оказалось, что народ в большинстве своём удовлетворяется стандартными django.test.TestCase и django.test.Client

.

Django sane testing

Sane testing закрепляет стратегию тестирования “нужна БД — django.test.TestCase, можно обойтись без БД — unittest.TestCase” и делает ее более явной. Sane testing предоставляет базовые классы для тестирования в стиле xUnit в следующих вариациях:

  • если тестовая БД не требуется, то UnitTestCase
  • если требуется тестовая БД, но каждый тест можно “обернуть” транзакцией и после успешного выполнения теста эту транзакцию откатить — DatabaseTestCase (сюда же относятся и тесты, использующие django.test.Client)
  • если нужна тестовая БД и тесты используют транзакции (в этом случае, БД сбрасывается для каждого теста) — DestructiveDatabaseTestCase
  • если для тестов нужен http-сервер (например, для проверки http basic/digest аутентификации) — то HttpTestCase (он же является и деструктивным для БД)
  • для функциональных тестов при помощи Selenium RCSeleniumTestCase

Django test utils

Django-test-utils примечателен оригинальными идеям, однако реализация хромает. Первым, чем выделяются test utils — генератор функциональных тестов. Вы запускаете manage.py testmaker, запускается обычный dev-http сервер, ходите по ссылкам, а testmaker записывает ваши действия. Потом вы останавливаете dev-http сервер и вуаля — у вас есть сгенерированные тесты. Идея хороша. Для twill есть инструменты генерирования тестов, но не нативные, а использующие более общие генераторы веб-тестов. Для Selenium есть родные инструменты подготовки тестов “натыкиванием”, но сам Selenium, IMHO, не очень удобно запускать (по крайней мере, в Django-инфраструктуре). Но тесты, которые создает testmaker, мне не особо понравились: и стиль кода выпадает (например, там для отступов используется tab), и сами тесты (в содержательной части) мне не особо понравились.

Дальше больше и test utils предоставляют еще один интересный инструмент — краулер. Он считывает urlconf, потом ходит по объявленным ссылкам (с опциональной возможностью записывать время отклика) и отчитывается, на какие урлы из urlconf он ни разу не ходил. Бывает полезно ;)

На этом дело не заканчивается, и test utils еще дают небольшую интеграцию Django и twill, взятую у djutils.test. Правда, взята бездарно, потому что testmaker не умеет генерировать тесты для twill.

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

Django satprep

Django satprep минималистичен: это nose test runner (взятый из basie) и небольшой полезный модуль для nose+twill, делающий преднастройку WSGI intercept. В двух словах: идея WSGI intercept в том, что для функционального тестирования используется не полноценный http-клиент и http-сервер, а как конечная цель тестирования используется WSGI приложение.

djutils.test

Djutils — эта куча всякого Django-related кода. Я не стал толком рассматривать все его фичи, а сконцентрировался на тестировании, так что рассматриваем дальше только djutils.test. Выше этот пакет уже упоминался, как оригинальное место, откуда заимствована интеграция Django и twill. Интеграция заключается в

  1. По сайту можно ходить по относительным ссылкам, так что не нужно привязываться к хосту и порту для twill
  2. Возможность использовать reverse-resolving вместе урлов
  3. Возможность для аутентификации давать экземпляр django.contrib.auth.User

Из оставшихся фич: nose test runner (своя реализация) и py.test runner.

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

NoseDjango

В NoseDjango применен метод “от противного”. Большинство проектов используют возможности Django для применения кастомных test runner’ов. NoseDjango наоборот, использует расширяемость nose и реализован в виде nose-плагина. Инсталляция NoseDjango добавляет в nosetests опцию --with-django и настраивает тестовое окружения Django перед запуском тестов. В принципе, работает как заявлено, но мне пока что больше нравится запускать джанговские тесты при помощи python manage.py test, хотя может и этот плагин распробую…

tddspry

Подход tddspry напоминает подход Django sane testing — явно разделенные базовые классы тестов:

  • NoseTestCase — полная аналогия unittest.TestCase, не использует БД
  • DatabaseTestCase — тесты, которым нужны БД
  • HttpTestCase — twill-тесты, также есть кое-какие хелперы.

В общем и целом, создалось впечатление “почти django sane testing с twill вместо selenium”.

Что я выбрал и почему

Когда я начинал делать обзор, я искал инструмент, который позволил бы мне:

  1. Использовать уже написанные тесты, которые на момент анализа запускались python manage.py test
  2. Писать некоторые тесты в nose-стиле (т.е. assert’ами, а не используя xUnit API). Это не потому, что мне не нравится xUnit API, а потому что кое-какие тесты проще писать и поддерживать именно в nose-стиле.
  3. Первые два пункта были критическими, а этот пункт опциональным: по возможности дать привязки к twill (меня больше интересовал вопрос автоматического обхода набора twill-тестов, чем Django-интеграция и улучшенный API для twill’а).

Так вот, ни один из просмотренных инструментов не подходил под требования пункта 1. Т.е. стандартный джанговский test runner нормально запускал тесты, я переключался на альтернативный test runner, и тот не видел половину тестов.

Немного проясню причины. Дело в том, что стандартный django.test.simple.run_tests обходит все установленные приложения и ищет тесты в оговоренных местах: в модуле моделей и в модуле/пакете <app>.tests. Все рассмотренные здесь инструменты используют поиск тестов средствами nose. Таким образом, если у вас есть проект и подключены приложения вне дерева кода этого проекта, то джанговский test runner их запускает, а nose test discovering их не находит.

Поэтому я взял наиболее простой django-satprep и адаптировал его под мои требования. Пункт 2 выполнился автоматически, а в качестве бонуса nose ловит тесты в тест-пакете, которые и не перечислены в __init__.py внутри <app>.tests. По правде сказать, адаптация не даёт мне чувства законченности и у нее есть неизящные решения, наследованные от satprep (например, опции для nose передаются в виде python manage.py test -- -vds), но она удовлетворяет текущим требованиям, а дальше будет видно, то ли переключаться на NoseDjango, то ли дальше “дотачивать” свой форк satprep.

А про twill подробнее я расскажу в другой раз ;)

Подписаться Комментировать

Комментарии

14.09.2009 4:34 Иван Сагалаев

Самого главного не написал :-) Чем тебя встроенный фреймворк тестов не устраивает?

14.09.2009 7:38 Юревич Юрий

пункт 2. Мне нужны были тесты в стиле nose.

В смысле, не только assert вместо self.assert_, но и профайлер, покрытие.

14.09.2009 12:53 Юревич Юрий

Опа. Мимо меня прошел Django Test Extensions.

Форма комментирования для «Внешние инструменты тестирования для Django»

Обязательное поле. Не больше 30 символов.

Обязательное поле

Пожалуйста, введите символы, которые вы видите на изображении

21.09.2009 22:49 oduvan

и как он вам?

14.09.2009 18:26 Big 40wt Svetlyak

А я запускаю nosetests через djangorecipe для buildout, и он почему-то находит все нужные тесты.

14.09.2009 18:50 Юревич Юрий
  1. Я до zc.buildout пока не добрался :) Если расскажешь чуть подробнее — будет очень здорово, я давно присматриваюсь к buildout, да нет хорошего повода попробовать его всерьез. Точнее так. Я сам разберусь с тем как (плюс же еще есть туториал от JKM, мне бы прочувствовать зачем оно мне нужно и что даст ;) Т.е. достаточно чтобы ты рассказал, какие бенефиты тебе даёт и какой workflow у тебя при разработке/деплое.
  2. Он находит нужные тесты, потому что весь код находится в одном дереве — в дереве контейнера buildout, положи куда-нибудь в другое место, доступное из sys.path — увидишь.
14.09.2009 19:56 Big 40wt Svetlyak

zc.buildout имеет преимущества перед тем же virtualenv, за счет того, что легко расширяется собственными рецептами и уже имеет кучу готовых.

Для меня он полностью выстраивает окружение для запуска сайта, при этом, есть возможность написать конфиг для девелопмента, который, скажем, не будет вытягивать некоторые пакеты из PyPi, а будет использовать мои локальные директории.

Как именно он находит тесты я не копал, но факт, что не все исходники при этом внутри buildout директории. Единственное, что эти app он добавляет в sys.path.

Обычный цикл у меня такой:

  1. Я что-то делаю, коммичу.
  2. Запускаю fab production update restart.
  3. Всё :)