OpenQuality.ru

Качество программного обеспечения

Качество программного обеспечения: в главных ролях

Лента  Радар  Блог  Опыт  
Разум  Видео  Заметки  Эпизоды


Python doctest: мал золотник да дорог

Здравствуйте.

Модуль doctest, входящий в стандартную библиотеку Python, удобен при решении следующих задач:

– создание и прогон регрессионных unit-тестов
– документирование программного кода

Рассмотрим небольшой пример. В модуле transformation.py определена функция is_palindrome, которая находит слова-палиндромы в заданной строке. Ожидаем, что слова разделены пробелами, а пустая строка недопустима:

def is_palindrome (s):
        if len(s) < 1:
                raise ValueError ("Empty String!")
        words = s.split(" ")
        for word in words:
                wlist = list(word)
                wlist.reverse()
                drow = ''.join(wlist)
                if word == drow:
                        print word

Обратимся к is_palindrome из оболочки Python:

>>> from transformation import is_palindrome
>>> is_palindrome("level hello racecar")
level
racecar
>>> is_palindrome("")
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "transformation.py", line 12, in is_palindrome
    raise ValueError ("Empty String!")
ValueError: Empty String!
>>>

Функция выводит найденные палиндромы, а в случае пустой строки инициирует исключение. Полученные результаты могут одновременно служить unit-тестами и комментариями к is_palindrome. Видоизменим текст функции, добавив в нее результаты прогона и вызов unit-тестов:

def is_palindrome (s):
        """ Print palindromes
        >>> is_palindrome("level hello racecar")
        level
        racecar
        >>> is_palindrome("")
        Traceback (most recent call last):
                ...
        ValueError: Empty String!
 
        """
        if len(s) < 1:
                raise ValueError ("Empty String!")
        words = s.split(" ")
        for word in words:
                wlist = list(word)
                wlist.reverse()
                drow = ''.join(wlist)
                if word == drow:
                        print word
 
def simple_test():
        import doctest
        doctest.testmod()
 
if __name__ == "__main__":
        simple_test()

Результат запуска transformation.py из командной строки будет более чем скромным:

[capt@rh Work]# python transformation.py
[capt@rh Work]#

Такой результат означает, что все тесты, представленные в модуле, выполнены успешно. Подробные сведения можно получить с помощью ключа -v:

[capt@rh Work]# python transformation.py -v
Running __main__.__doc__
0 of 0 examples failed in __main__.__doc__
Running __main__.is_palindrome.__doc__
Trying: is_palindrome("level hello racecar")
Expecting:
level
racecar
ok
Trying: is_palindrome("")
Expecting:
Traceback (most recent call last):
        ...
ValueError: Empty String!
ok
0 of 2 examples failed in __main__.is_palindrome.__doc__
Running __main__.simple_test.__doc__
0 of 0 examples failed in __main__.simple_test.__doc__
2 items had no tests:
    __main__
    __main__.simple_test
1 items passed all tests:
   2 tests in __main__.is_palindrome
2 tests in 3 items.
2 passed and 0 failed.
Test passed.

Успешно пройдены два теста. Убедимся в том, что при изменении алгоритма прогон тестов закончится неудачей. Для этого в функции is_palindrome закомментируем одну строку:

wlist = list(word)
# wlist.reverse()
drow = ''.join(wlist)

Запуск transformation.py:

[capt@rh Work]# python transformation.py
********************************************************
Failure in example: is_palindrome("level hello racecar")
from line #1 of __main__.is_palindrome
Expected:
level
racecar
Got:
level
hello
racecar
********************************************************
1 items had failures:
   1 of   2 in __main__.is_palindrome
***Test Failed*** 1 failures.

Результат соответствует ожидаемому.

Дополнительную информацию о модуле doctest можно почерпнуть в документации Python.

Отметим плюсы и минусы doctest:

+ входит в стандартную библиотеку
+ простота использования
+ unit-тесты одновременно служат комментариями к коду
– необходимость жесткого соблюдения формата вывода (что не всегда приемлемо)
+ возможность интеграции с unittest (еще одним встроенным механизмом организации unit-тестов)

Разговор о unit-тестах в Python будет продолжен в одной из следующих статей. Оставайтесь с нами.

Отправить в Twitter, Facebook, FriendFeed, ВКонтакте | Опубликовано 20.01.2009 в рубрике "Модульные тесты"

Комментарии (2)

  1. Pingback : OpenQuality.ru | Python unittest: базовые возможности | February 2, 2009

    […] Приложение, достойное модульного тестирования, как правило содержит больше одного модуля. Соответственно, unittest предоставляет возможности для централизованного управления всеми тестами. Создадим агрегатор, который будет запускать тесты из класса TestBankAccount и тесты нахождения палиндромов. […]


  2. Pingback : OpenQuality.ru | Python: модульное тестирование с MiniMock | June 2, 2009

    […] впечатлением от модуля python-mock. Будучи приверженцем doctest, Ian решил задействовать его возможности для […]



Добавить комментарий

Пожалуйста, исправьте результат: дважды два равно



КРАТКОЕ СОДЕРЖАНИЕ

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


ПУТЕВОДИТЕЛЬ

Список всех статей с краткой аннотацией и разбивкой по рубрикам. Открыть карту.

ПОДПИСКА

Доступ к самым интересным материалам по электропочте и RSS. Подробности.

ИЩЕЙКА