Документация Python неофициальный перевод
Содержание страницы

test – пакет регрессионных тестов для Pythontest – Regression tests package for Python

Пакет test содержит все регрессионные тесты для Python, а также модули test.test_support и test.regrtest. test.test_support используется для расширения возможностей тестов, в то время как test.regrtest управляет тестовым набором.

Каждый модуль в пакете test, имя которого начинается с test_, является набором тестов для определённого модуля или функции. Все новые тесты должны быть написаны с использованием модуля unittest или doctest. Некоторые старые тесты написаны в «традиционном» стиле, который сравнивает вывод, отправленный в sys.stdout; такой стиль считается устаревшим.

См. также

Модуль unittest
Написание регрессионных тестов PyUnit.
Модуль doctest
Тесты, встроенные в строки документации.

Написание модульных тестов для пакета testWriting Unit Tests for the test package

Рекомендуется, чтобы тесты, использующие модуль unittest, следовали нескольким рекомендациям. Одна из них – называть тестовый модуль, начиная его с test_ и заканчивая именем тестируемого модуля. Методы тестирования в тестовом модуле должны начинаться с test_ и заканчиваться описанием того, что тестирует метод. Это необходимо, чтобы драйвер тестов распознавал методы как тестовые. Кроме того, не следует включать строку документации для метода. Вместо этого следует использовать комментарий (например, # Тестирует функция возвращает только True или False) для документирования тестовых методов. Это сделано потому, что строки документации выводятся на печать, если они существуют, и, таким образом, не указывается, какой именно тест выполняется.

Часто используется стандартная заготовка:

import unittest
from test import test_support

class MyTestCase1(unittest.TestCase):

    # Only use setUp() and tearDown() if necessary

    def setUp(self):
        ... code to execute in preparation for tests ...

    def tearDown(self):
        ... code to execute to clean up after tests ...

    def test_feature_one(self):
        # Test feature one.
        ... testing code ...

    def test_feature_two(self):
        # Test feature two.
        ... testing code ...

    ... more test methods ...

class MyTestCase2(unittest.TestCase):
    ... same structure as MyTestCase1 ...

... more test classes ...

def test_main():
    test_support.run_unittest(MyTestCase1,
                              MyTestCase2,
                              ... list other tests ...
                             )

if __name__ == '__main__':
    test_main()

Этот шаблонный код позволяет запускать тестовый набор как с помощью test.regrtest, так и самостоятельно в качестве скрипта.

Цель регрессионного тестирования – попытаться сломать код. Это приводит к нескольким правилам, которым следует следовать:

  • Набор тестов должен проверять все классы, функции и константы. Это включает не только внешний API, который будет представлен внешнему миру, но и «приватный» код.

  • Предпочтение отдается тестированию «белого ящика» (анализ тестируемого кода при написании тестов). Тестирование «черного ящика» (тестирование только опубликованного пользовательского интерфейса) недостаточно полно, чтобы убедиться, что все граничные и крайние случаи протестированы.

  • Убедитесь, что проверены все возможные значения, включая недопустимые. Это гарантирует, что не только все корректные значения принимаются, но и неправильные значения обрабатываются корректно.

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

  • Добавляйте явные тесты для любых ошибок, обнаруженных в тестируемом коде. Это гарантирует, что ошибка не возникнет снова, если код будет изменён в будущем.

  • Убедитесь, что после тестов выполнена очистка (например, закрыты и удалены все временные файлы).

  • Если тест зависит от конкретного условия операционной системы, то проверьте, что условие уже выполняется, прежде чем запускать тест.

  • Импортируйте как можно меньше модулей и делайте это как можно раньше. Это минимизирует внешние зависимости тестов, а также уменьшает возможные аномальные поведения из-за побочных эффектов импорта модуля.

  • Старайтесь максимально повторно использовать код. Иногда тесты различаются такими мелочами, как тип входных данных. Минимизируйте дублирование кода, создавая подкласс базового тестового класса, который задаёт входные данные:

    class TestFuncAcceptsSequences(unittest.TestCase):
    
        func = mySuperWhammyFunction
    
        def test_func(self):
            self.func(self.arg)
    
    class AcceptLists(TestFuncAcceptsSequences):
        arg = [1,2,3]
    
    class AcceptStrings(TestFuncAcceptsSequences):
        arg = 'abc'
    
    class AcceptTuples(TestFuncAcceptsSequences):
        arg = (1,2,3)
    

См. также

Разработка через тестирование
Книга Кента Бека о написании тестов до кода.

Запуск тестов с помощью test.regrtestRunning tests using test.regrtest

test.regrtest можно использовать как скрипт для запуска набора регрессионных тестов Python. Запуск скрипта без аргументов автоматически запускает все регрессионные тесты из пакета test. Для этого находятся все модули пакета, имена которых начинаются с test_, они импортируются, и выполняется функция test_main(), если она присутствует. Имена тестов для запуска также можно передать скрипту. Указание одного регрессионного теста (python regrtest.py test_spam.py) минимизирует вывод и покажет только, пройден тест или нет, тем самым уменьшая объём вывода.

Запуск test.regrtest напрямую позволяет установить, какие ресурсы доступны для использования тестами. Это делается с помощью опции командной строки -u. Выполните python regrtest.py -uall, чтобы включить все ресурсы; указание all в качестве параметра для -u включает все возможные ресурсы. Если нужны все ресурсы, кроме одного (более типичный случай), после all можно перечислить через запятую ресурсы, которые не нужны. Команда python regrtest.py -uall,-audio,-largefile запустит test.regrtest со всеми ресурсами, кроме audio и largefile. Чтобы увидеть список всех ресурсов и дополнительные опции командной строки, выполните python regrtest.py -h.

Некоторые другие способы выполнения регрессионных тестов зависят от платформы. В Unix можно выполнить make test в каталоге верхнего уровня, где была собрана Python. В Windows запуск rt.bat из каталога PCBuild запустит все регрессионные тесты.

test.support – Вспомогательные функции для тестовtest.support – Utility functions for tests

Модуль test.support предоставляет поддержку для регрессионных тестов Python.

В этом модуле определены следующие исключения:

exception test.support.TestFailed
Исключение, возбуждаемое при сбое теста. Это устарело в пользу тестов на основе unittest и методов утверждений unittest.TestCase.
exception test.support.TestSkipped
Подкласс TestFailed. Возникает, когда тест пропускается. Это происходит, когда необходимый ресурс (например, сетевое соединение) недоступен на момент тестирования.
exception test.support.ResourceDenied
Подкласс TestSkipped. Возникает, когда ресурс (например, сетевое соединение) недоступен. Вызывается функцией requires().

Модуль test.support определяет следующие константы:

test.support.verbose
True, если включён подробный вывод. Следует проверять, когда требуется более детальная информация о выполняющемся тесте. verbose устанавливается test.regrtest.
test.support.is_jython
True, если текущий интерпретатор – Jython.
test.support.TESTFN
Устанавливается в путь, по которому может быть создан временный файл. Любой созданный временный файл должен быть закрыт и удалён.

Модуль test.support определяет следующие функции:

test.support.forget(module_name)
Удаляет модуль с именем module_name из sys.modules и удаляет любые откомпилированные файлы этого модуля.
test.support.is_resource_enabled(resource)
Возвращает True, если resource включён и доступен. Список доступных ресурсов устанавливается только тогда, когда test.regrtest выполняет тесты.
test.support.requires(resource[, msg])
Вызывает ResourceDenied, если resource недоступен. msg является аргументом для ResourceDenied, если он вызывается. Всегда возвращает true, если вызывается функцией, __name__ которой равно '__main__'. Используется, когда тесты выполняются с помощью test.regrtest.
test.support.findfile(filename)
Возвращает путь к файлу с именем filename. Если совпадение не найдено, возвращается filename. Это не считается ошибкой, поскольку это может быть путь к файлу.
test.support.run_unittest(*classes)

Выполняет подклассы unittest.TestCase, переданные в функцию. Функция сканирует классы на наличие методов, начинающихся с префикса test_, и выполняет тесты по отдельности.

Также допускается передача строк в качестве параметров; они должны быть ключами в sys.modules. Каждый соответствующий модуль будет просканирован с помощью unittest.TestLoader.loadTestsFromModule(). Обычно это встречается в следующей функции test_main():

def test_main():
    test_support.run_unittest(__name__)

Это запустит все тесты, определенные в указанном модуле.

test.support.check_warnings()

Удобная обёртка для warnings.catch_warnings(), которая упрощает проверку того, что предупреждение было правильно вызвано с помощью одной проверки. Она примерно эквивалентна вызову warnings.catch_warnings(record=True).

Основное отличие в том, что при входе в менеджер контекста возвращается экземпляр WarningRecorder, а не простой список. Базовый список предупреждений доступен через атрибут warnings объекта-регистратора, а атрибуты последнего вызванного предупреждения также доступны напрямую через этот объект. Если ни одного предупреждения не было вызвано, то все эти атрибуты будут равны None.

На объекте-регистраторе также предоставляется метод reset(). Этот метод просто очищает список предупреждений.

Менеджер контекста используется следующим образом:

with check_warnings() as w:
    warnings.simplefilter("always")
    warnings.warn("foo")
    assert str(w.message) == "foo"
    warnings.warn("bar")
    assert str(w.message) == "bar"
    assert str(w.warnings[0].message) == "foo"
    assert str(w.warnings[1].message) == "bar"
    w.reset()
    assert len(w.warnings) == 0
test.support.captured_stdout()

Это менеджер контекста, который выполняет тело оператора with, используя объект StringIO.StringIO в качестве sys.stdout. Этот объект можно получить с помощью предложения as оператора with.

Пример использования:

with captured_stdout() as s:
    print("hello")
assert s.getvalue() == "hello"

Модуль test.support определяет следующие классы:

class test.support.TransientResource(exc[, **kwargs])
Экземпляры представляют собой менеджер контекста, который генерирует исключение ResourceDenied, если возникает указанный тип исключения. Все именованные аргументы обрабатываются как пары атрибут/значение для сравнения с любым исключением, возникшим внутри блока with. Исключение ResourceDenied возбуждается только в том случае, если все пары правильно совпадают с атрибутами исключения.
class test.support.EnvironmentVarGuard
Класс, используемый для временной установки или снятия переменных окружения. Экземпляры можно использовать как менеджер контекста.
EnvironmentVarGuard.set(envvar, value)
Временно устанавливает переменную окружения envvar в значение value.
EnvironmentVarGuard.unset(envvar)
Временно сбрасывает переменную окружения envvar.
class test.support.WarningsRecorder
Класс, используемый для записи предупреждений в модульных тестах. Дополнительные сведения см. в описании check_warnings() выше.