Python, doctest. Тестирование и документирование разрабатываемого кода.

Введение

При разработке кода необходимо делать две вещи. Документировать код и писать для него тесты. Python предоставляет для этого простой и совершенно замечательный инструмент - библиотеку doctest.

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

В самом деле, мы же все равно во время отладки вызываем, например отлаживаемую функцию, т.е. пишем код тестирования. Почему бы не писать тесты прямо во время отладки?

Простейший пример тестирования функции

Проще всего пояснить, как это делается на примере. Ниже приведен простейший пример функции вместе с тестом для ее проверки. Как в этом тексте все сделано:

  • В строках комментариев к функции написан тест. Тесты могут чередоваться с обычным текстом.
  • Строчки комментария, начинающиеся с '>>>' выполняются
  • В первой строчке, которая следует за строчками с '>>>', стоит результат, с которым будет сравнен с возвращаемый выражением в последней исполненной строке результатом. Если результаты равны, ничего не произойдет, если не равны, будет выдано сообщение об ошибке теста.
  • Тестирование вывается вызовом функции doctest.testmod()
  • Если хотим посмотреть, что происходит, можно вызвать тест в "разговорчивом" режиме: doctest.testmod(verbose=True)
  • Для выполнения тестов, запускаем скрипты примеров на выполнение.
#! /usr/bin/python
# -*- coding: utf-8 -*-

def mult(a,b):
    """
    >>> mult(2,2)
    4
    """
    return a*b
    
if __name__ == "__main__":
    import doctest
    doctest.testmod()
    #doctest.testmod(verbose=True)

Пример тестирования класса

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

#! /usr/bin/python
# -*- coding: utf-8 -*-

class A():
    """
    >>> a = A()
    >>> a.atr
    4
    """

    atr = 4

    def mult(self,a):
        """
        >>> a = A()
        >>> a.mult(2)
        8
        """
        return a * self.atr
    
if __name__ == "__main__":
    import doctest
    doctest.testmod(verbose=False)
    #doctest.testmod(verbose=True)

Пример с тестами в отдельном файле

Все напсанное выше замечательно, но когда тестов становится больше, они начинают слишком загромождать код и появляется желание отделить их от кода. Это тоже предусмотрено. Можно вызвать функцию doctest.testfile("имя файла") и она выполнит тесты из внешнего файла. Ниже приведен пример такого подхода.

Файл с тестируемым кодом:

#! /usr/bin/python
# -*- coding: utf-8 -*-

def mult(a,b):
    return a*b
    
if __name__ == "__main__":
    import doctest
    doctest.testfile("test.txt")
    #doctest.testfile("test.txt",verbose=True)

Файл тестов:

Тестирование функции mult(a,b)

>>> from test_in_other_file import mult
>>> mult(2,3)
6

Разные полезности

Печать информации в ходе теста

>>> import sys
>>> print >> sys.stderr, "----- Testing of the connection -----"

Проверка равенства двух величин

>>> doct_n == doct_r
True

Запуск тестов из внешнего скрипта или консоли

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

  • Запускаем консоль с Python
  • Импортируем doctest
  • Импортируем тестируемый модуль
  • Запускаем тесты командой doctest.testmod(имя тестируемого модуля) или doctest.testmod(имя тестируемого модуля,verbose=True)

Пример:

In [1]: import doctest
In [2]: import function
In [3]: doctest.testmod(function,verbose=True)
Trying:
    mult(2,2)
Expecting:
    4
ok
1 items had no tests:
    function
1 items passed all tests:
   1 tests in function.mult
1 tests in 2 items.
1 passed and 0 failed.
Test passed.
  

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

Но ничего, на этот случай есть модуль imp. А у него есть команда imp.reload(), которая принудительно перегружает указанный модуль. Итак теперь перед повторной проверкой подаем команду:

In [1]: import imp
In [2]: import doctest
In [3]: import function
In [4]: doctest.testmod(function)
In [6]: imp.reload(function)
Out[7]: 
In [4]: doctest.testmod(function)

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

Заключение и ссылки

По моему приведенных примеров вполне достаточно, чтобы понять суть и начать работать с doctest. Если нужна дополнительная информация, то все есть в документации:

25.2. doctest — Test interactive Python examples

Опубликовано: September 9, 2010

Комментарии:


Имя: Anonymous

Отличная статья.



Имя: Dmitry

Превосходное введение, довольно сильно сэкономил время, прочитав данную статью перед документацией! Респект автору!



Имя: test

test



Имя: test1

test1



Имя: test3

<a href="#">test</a>



Имя:

оригинальная защита от спама



Имя: Anatoly

Хорошо написано, только вот текст в левой колонке мелковато выглядит на экране ноутбука 14".



Имя: bayah

Отлично, все ясно!



Комментировать:

Имя:

Комментарий: