Nose and Python

Введение

Test Driven Development (Разработка через тестирование) — одна из базовых техник экстремального программирования. На практике она мало подходит для небольших проектов, но при правильном применении — ускоряет и упрощает разработку и средних и крупных. Однако, пример лучше давать на чём-то несильно структурированном — поэтому он, поэтапный, таковым и будет :). Подробнее о самой технике можно прочитать по ссылкам, а я рассмотрю её c позиции модуля nose для Python.

Почему nose? В основном потому, что в стандартной поставке Python идёт довольно-таки странный модуль unittest, который был практически скопирован с Java-версии библиотеки unit-тестов JUnit — немного позже, чем та сама появилась. В связи с этим, использование модуля приводит даже к тому, что нарушаются принципы самого языка Python — простота, скорость и свобода :). В целом, unittest — это повсеместные классы и Java-подобные вызовы .assertNotEquals вместе с друзьями, а nose — это использование зарезервированного слова assert, власть над месторасположением тестов, тесты-функции и пр., пр., пр… Ну и сверх того, nose поддерживает плагины, которые могут пригодиться для самых различных целей.

Ссылки

Установка

При установленных setuptools процесс сводится к одному действию в командной строке:

easy_install nose

Чтобы установить setuptools (setuptools — утилита для лёгкой установки всяческих пакетов для Python, в том числе пакетов, поставляющихся в виде яиц) нужно предварительно скачать файл ez_setup.py и запустить в командной строке, всё необходимое скачается и установится самостоятельно:

python ./ez_setup.py

Другой способ — скачать архив исходников, распаковать его и выполнить команду:

python setup.py install

Пользователи Ubuntu могут также воспользоваться командой apt-get python-nose.

После этого можно импортировать пакет nose в ваш .py-файл и/или использовать утилиту nosetests (она находится в каталоге $PYTHON/Scripts/nosetests) которая сама находит все тесты (в том числе и тестовые функции, см.) в каталоге, из которого запущена, и выводит результат их запуска (с использованием дополнительных опций и плагинов можно, например, проверить покрытие тестов; список опций выводится при запуске nosetests --help, подробнее о них — см. прим. 2).

План

Подготовка

Создадим два класса — тест, который будет проверять ненаписанный ещё код на выполнение указанных выше условий и на корректную работу (то есть по завершению он заодно будет, в некоторой степени, документацией к классу1) и, конечно, сам класс, реализующий наш план — назовём его srange.

Тест будет находится в файле testsrange.py. (Эта заготовка позволяет эмулировать при необходимости тесты unittest — например, в IDE со специальным диалогом для проверки тестов, таких как PyScripter, PyDev, Eric, Boa… (работа с PyScripter кратко описана в прим. 1)).

#!/usr/bin/env/python
#-*- coding: utf-8 -*-
 
import unittest
import nose
#from srange import srange
 
class TestSRange(unittest.TestCase):
    pass
 
def run():
    unittest.main(module="testsrange")
 
if __name__ == '__main__':
    run()

Класс — в файле srange.py.

#!/usr/bin/env/python
#-*- coding: utf-8 -*-
 
class srange:
    pass

После их создания, можно раскомментировать строку импорта в testsrange.py:

from srange import srange

Теперь тест можно запускать либо командой python ./testsrange.py, либо с использованием утилиты nosetests, соответствующей командой: nosetests -v (для Windows необходимо будет указать путь к утилите). Ключ -v описан в прим. 2 и принуждает команду nosetests выводить более подробную информацию о тестах — например имена методов, которые были запущены и статус. Считаю, что на эту информацию не стоит закрывать глаза — она понадобится при отладке. Однако в общих случаях команду nosetests рекомендуется запускать без ключей.

Результат пока радует — 0 из 0 тестов прошли:

----------------------------------------------------------------------
Ran 0 tests in 0.015s

OK

Ниже процесс описан в виде поэтапной цепочки кусок_теста-кусок_реализации-кусок_теста-кусок_реализации-…. Её можно рассматривать как последовательно, так и параллельно — в последнем случае: написать полностью сам тест, а затем сконструировать по нему реализацию.

Написание теста, шаг 1

Реализация, шаг 1

Написание теста, шаг 2

Реализация, шаг 2

Написание теста, шаг 3

Реализация, шаг 3

Полный текст srange.py

Полный текст testsrange.py

Заключение

Это не то чтобы правильный окончательно (с такими утверждениями в TDD вообще сложно), но близкий к истине пример использования методики TDD, кроме того показывающий взаимодействие nose и Python. В реальной жизни, всё же, многие из шагов опускаются, ввиду очевидности того, что они несут. Выбор за вами — главное, не упустить важное :).

Примечание 1. Использование PyScripter

Примечание 2. Опции утилиты nosetests

python path

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License