Spec-Zone.ru › Python 2.7

test — Пакет регрессионных тестов для Python

Примечание

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

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

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

См. также

Module unittest

Написание регрессионных тестов PyUnit.

Module doctest

Тесты, встроенные в строки документации.

1. Написание модульных тестов для пакета test

Желательно, чтобы тесты, использующие модуль unittest, следовали нескольким рекомендациям. Одна из них — именовать модуль тестов, начиная с test_ и заканчивая именем тестируемого модуля. Методы тестов в модуле тестов должны начинаться с test_ и заканчиваться описанием того, что тестирует метод. Это необходимо для того, чтобы драйвер тестов распознавал их как методы тестов. Также не следует включать строку документации для метода. Комментарий (например, # Tests function returns only True or False) должен использоваться для предоставления документации для методов тестов. Это делается потому, что строки документации выводятся, если они существуют, и поэтому не указывается, какой тест выполняется.

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

import unittest
from test import 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():
    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 Driven Development

Книга Кента Бека о написании тестов до кода.

2. Запуск тестов с помощью командной строки

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

Запуск test.regrtest напрямую позволяет настроить доступные ресурсы для тестов. Для этого используется опция командной строки -u. Указание all в качестве значения для опции -u включает все возможные ресурсы: python -m test.regrtest -uall. Если требуется все, кроме одного ресурса (более распространённый случай), можно указать список ресурсов, которые не требуются, через запятую после all. Команда python -m test.regrtest -uall,-audio,-largefile запустит test.regrtest со всеми ресурсами, кроме ресурсов audio и largefile. Для просмотра списка всех ресурсов и дополнительных опций командной строки, выполните python -m test.regrtest -h.

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

Изменено в версии 2.7.14: Пакет test может быть запущен как скрипт: python -m test. Это работает так же, как запуск модуля test.regrtest.

test.support — Утилитарные функции для тестов

Примечание

Модуль test.test_support был переименован в test.support в Python 3.x и 2.7.14. Имя test.test_support сохранено как псевдоним в 2.7.

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

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

exception test.support.TestFailed

Исключение, которое должно быть вызвано при ошибке теста. Это устарело в пользу тестов на основе unittest и методов утверждения unittest.TestCase.

exception test.support.ResourceDenied

Подкласс unittest.SkipTest. Вызывается, когда ресурс (например, сетевое соединение) недоступен. Вызывается функцией requires().

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

test.support.verbose

True, когда включён подробный вывод. Следует проверять при необходимости более подробной информации о выполняемом тесте. verbose устанавливается test.regrtest.

test.support.have_unicode

True, когда доступна поддержка Unicode.

test.support.is_jython

True, если интерпретатор — Jython.

test.support.TESTFN

Устанавливается в безопасное имя для временного файла. Любой созданный временный файл должен быть закрыт и удалён.

test.support.TEST_HTTP_URL

Определяет URL выделенного HTTP-сервера для сетевых тестов.

Модуль 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():
    support.run_unittest(__name__)

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

test.support.check_warnings(*filters, quiet=True)

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

check_warnings принимает 2-кортежи вида ("message regexp", WarningCategory) в качестве позиционных аргументов. Если один или несколько filters заданы, или если необязательный ключевой аргумент quiet равен False, он проверяет соответствие предупреждений ожиданиям: каждый указанный фильтр должен соответствовать хотя бы одному поднятому предупреждению, или тест завершается неудачно, и если подняты предупреждения, не соответствующие ни одному из указанных фильтров, тест завершается неудачно. Чтобы отключить первую проверку, установите quiet на True.

Если аргументы не указаны, используется по умолчанию:

check_warnings(("", Warning), quiet=True)

В этом случае все предупреждения перехватываются и не возникает ошибок.

При входе в менеджер контекста возвращается экземпляр WarningRecorder. Список предупреждений из catch_warnings() доступен через атрибут recorder объекта warnings. Для удобства, атрибуты объекта, представляющего последнее предупреждение, также доступны напрямую через объект recorder (см. пример ниже). Если предупреждение не было поднято, любые атрибуты, которые в противном случае ожидались у объекта, представляющего предупреждение, вернут None.

Объект recorder также имеет метод reset(), который очищает список предупреждений.

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

with check_warnings(("assertion is always true", SyntaxWarning),
                    ("", UserWarning)):
    exec('assert(False, "Hey!")')
    warnings.warn(UserWarning("Hide me!"))

В этом случае, если ни одно предупреждение не было поднято или было поднято какое-либо другое предупреждение, check_warnings() вызовет ошибку.

Когда тест требует более глубокого анализа предупреждений, а не просто проверки их появления, можно использовать код подобный этому:

with check_warnings(quiet=True) as w:
    warnings.warn("foo")
    assert str(w.args[0]) == "foo"
    warnings.warn("bar")
    assert str(w.args[0]) == "bar"
    assert str(w.warnings[0].args[0]) == "foo"
    assert str(w.warnings[1].args[0]) == "bar"
    w.reset()
    assert len(w.warnings) == 0

Здесь все предупреждения будут перехвачены, и код теста непосредственно проверяет перехваченные предупреждения.

Добавлено в версии 2.6.

Изменено в версии 2.7: Добавлены новые необязательные аргументы filters и quiet.

test.support.check_py3k_warnings(*filters, quiet=False)

Аналогично check_warnings(), но для предупреждений совместимости с Python 3. Если sys.py3kwarning == 1, проверяется, было ли предупреждение фактически поднято. Если sys.py3kwarning == 0, проверяется, что предупреждение не было поднято. Принимает 2-кортежи вида ("message regexp", WarningCategory) в качестве позиционных аргументов. Когда необязательный ключевой аргумент quiet равен True, тест не завершается неудачно, если фильтр ничего не поймал. Без аргументов используется по умолчанию:

check_py3k_warnings(("", DeprecationWarning), quiet=False)

Добавлено в версии 2.7.

test.support.captured_stdout()

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

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

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

Добавлено в версии 2.6.

test.support.import_module(name, deprecated=False)

Эта функция импортирует и возвращает указанный модуль. В отличие от обычного импорта, эта функция вызывает unittest.SkipTest, если модуль не может быть импортирован.

Сообщения об устаревании модулей и пакетов подавляются во время этого импорта, если deprecated равен True.

Добавлено в версии 2.7.

test.support.import_fresh_module(name, fresh=(), blocked=(), deprecated=False)

Эта функция импортирует и возвращает свежую копию указанного модуля Python, удалив указанный модуль из sys.modules перед выполнением импорта. В отличие от reload(), исходный модуль при этом не затрагивается.

fresh — итерируемый список дополнительных имён модулей, которые также удаляются из кэша sys.modules перед импортом.

blocked — итерируемый список имён модулей, которые заменяются на 0 в кэше модулей во время импорта, чтобы гарантировать, что попытки их импорта вызывают ImportError.

Указанный модуль и любые модули, указанные в fresh и blocked, сохраняются перед началом импорта и затем снова вставляются в sys.modules после завершения свежего импорта.

Сообщения об устаревании модулей и пакетов подавляются во время этого импорта, если deprecated равен True.

Эта функция вызовет unittest.SkipTest, если указанный модуль не может быть импортирован.

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

# Get copies of the warnings module for testing without
# affecting the version being used by the rest of the test suite
# One copy uses the C implementation, the other is forced to use
# the pure Python fallback implementation
py_warnings = import_fresh_module('warnings', blocked=['_warnings'])
c_warnings = import_fresh_module('warnings', fresh=['_warnings'])

Добавлено в версии 2.7.

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

class test.support.TransientResource(exc[, **kwargs])

Экземпляры — менеджер контекста, который вызывает ResourceDenied, если указанный тип исключения поднят. Любые ключевые аргументы рассматриваются как пары атрибут/значение для сравнения с любым исключением, поднятым внутри оператора with. Только если все пары правильно соответствуют атрибутам исключения, поднимается ResourceDenied.

Добавлено в версии 2.6.

class test.support.EnvironmentVarGuard

Класс для временного задания или отключения переменных окружения. Экземпляры могут быть использованы как менеджеры контекста и имеют полный интерфейс словаря для запросов/изменений подлежащих os.environ. После выхода из менеджера контекста все изменения переменных окружения, выполненные через этот экземпляр, будут отменены.

Добавлено в версии 2.6.

Изменено в версии 2.7: Добавлен интерфейс словаря.

EnvironmentVarGuard.set(envvar, value)

Временно устанавливает переменную окружения envvar со значением value.

EnvironmentVarGuard.unset(envvar)

Временно удаляет переменную окружения envvar.

class test.support.WarningsRecorder

Класс, используемый для записи предупреждений в модульных тестах. Подробнее см. документацию check_warnings() выше.

Новая версия с 2.6.

© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/2.7/library/test.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API