Тестирование Python-приложений: от unittest к nose
Макс ИщенкоОпубликовано 20.02.2006 в Статьи
С тех пор, как появилась первая библиотека для модульного тестирования – JUnit – появилась необходимость удобного выполнения тестов и представления результатов. В классическом JUnit были т.н. “runners”, один для консоли, другой для графического режима. Современные Java-IDE обычно интегрируют процесс исполнения тестов обычно прямо в оболочку, благо в JUnit есть для этих целей API.
Когда в Python 2.1 появился модуль unittest его дизайн был фактически содран с JUnit, включая интерфейс нахождения тестов и TextRunner для выполнения тестов в консоли. Наверное любой Python-программист знаком с unittest.main(), assertEquals и unitest.TestCase. По крайней мере, у меня в Vim даже макросы есть специальные для набора этих повторяющихся конструкций.
Как это часто бывает с заимствованиями, удобство использования инструмента с “чуждой” идеологией оставляли желать лучшего. Одним не нравилось необходимость наследоваться от unitest.TestCase, другим – необходимость писать self.assertEquals() вместо привычного assert, третьим – негибкость в поиске и исполнении тестов.
В конце концов “терпець урвався” и на свет появилась первая реальная альтернатива unittest – py.test, как часть амбициозного проекта py lib. На мой взгляд, чересчур амбициозного, с невысокими шансами попасть в mainstream. Компонент py.test был и остается, пожалуй, самой популярной частью проекта.
Девиз py.test: “лучший API – его отсутствие”. Как следствие, исчез класс TestCase от которого необходимо было наследоваться, вместе со своими assertEquals методами.
Наконец-то появилась возможность использовать обычный assert, а тесты определялись по наличию приставки test_ в именах функций и файлов.
Главных проблем с py.test было две: неудобство установки (требовался полный SVN checkout проекта py с последующими махинациями с PATH/PYTHONPATH) и “too much magic”. Как я говорил, проект py был весьма “продвинутым” и использовал очень уж сомнительные “трюкачества” над интерпретатором. В результате иногда тесты не выполнялись или падали с какой-нибудь экзотической ошибкой.
Обе эти проблемы были решены при помощи nose. Эта поистине замечательная программка-кроха (<1,5KLoC) выполнена в лучших Unix-традициях: “do one thing and do it well”. В двух словах это “py.test done right”.
Магии в ней самый минимум, а устанавливается она при помощи сверх-удобной setuptools. В результате установки получаем скрипт “nosetests” который найдет и выполнит все наши тесты.
Помимо описанных выше функций, перешедших из py.test есть в ней и другие приятные мелочи, как-то: code coverage report (если установлен coverage.py), интеграция с doctest, “правильный” nosetest.main (оригинальный unittest.main использует sys.exit() что делает затруднительной интеграцию), package-level fixtures (определяются в __init__.py), интеграция с уже упомянутыми setuptools.
Собственно использование nose я решил оставить за рамками статьи т.к. документация описывает все что нужно.
P.S.: По большому счету, тестировать можно не только приложения написанные на Пайтоне, но практически это имеет смысл если Python уже используется каким-то образом. Например, когда тесты пишутся на Python, приложение на Си, а взаимодейстуют они через HTTP/командный интерфейс/API. Я, например, использую nose для тестирования веб-приложения при помощи twill.
Понравилась статья? Подпишись на обновления по RSS/E-mail


использование assert для тестирования имеет один неприятный нюанс: при запуске интерпретатора с опцией -O все assert отключаются.
Ну так не надо запускать интерпретатор с опцией -О.
Тем более для тестирования.
Это сильно сказано. А если нужно протестировать реальное поведение проги при этой опции? А еще есть гейзенбаги, я думаю вы о них знаете.
Насколько я знаю, эта опция мало на что влияет (помимо отключения ассертов) и соотв очень редко используется (во всяком случае я об этом не знаю). А насчет гейзенбагов в модульных тестах – это сильно сказано.
Обалденная штука. Раньше пользовал py.test, это – лучше. Кстати, никто не писал под него тесты для twisted? Может, есть уже для плагин, повторяющий функциональность twisted.trial?