Spec-Zone.ru › Python 3.6

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

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

26.8.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 ...

if __name__ == '__main__':
    unittest.main()

Этот шаблон кода позволяет запускать набор тестов test.regrtest, самостоятельно как сценарий, поддерживающий unittest CLI, или через python -m unittest CLI.

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

  • Набор тестов должен проверять все классы, функции и константы. Это включает не только внешний API, который должен быть представлен внешнему миру, но также и «приватный» код.
  • Предпочтителен белый ящик (исследование кода, тестируемого при написании тестов). Черный ящик (тестирование только опубликованного пользовательского интерфейса) недостаточно для проверки всех граничных и крайних случаев.
  • Убедитесь, что протестированы все возможные значения, включая недопустимые. Это гарантирует, что не только все допустимые значения приемлемы, но и что ненадлежащие значения обрабатываются правильно.
  • Исследуйте как можно больше путей кода. Проверьте, где происходит ветвление, и настройте входные данные, чтобы убедиться, что пройдено как можно больше различных путей по коду.
  • Добавьте явный тест на любые обнаруженные ошибки для тестируемого кода. Это гарантирует, что ошибка не появится снова, если код будет изменен в будущем.
  • Убедитесь, что вы очищаете после своих тестов (например, закрываете и удаляете все временные файлы).
  • Если тест зависит от определенного состояния операционной системы, проверьте, что состояние уже существует, прежде чем пытаться выполнить тест.
  • Импортируйте как можно меньше модулей и делайте это как можно раньше. Это сводит к минимуму внешние зависимости тестов и также сводит к минимуму возможное аномальное поведение из-за побочных эффектов импорта модуля.
  • Постарайтесь максимизировать повторное использование кода. В некоторых случаях тесты будут отличаться лишь небольшими деталями, такими как тип используемых входных данных. Минимизируйте дублирование кода, наследовавшись от базового класса тестов с классом, определяющим входные данные:

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

    При использовании этого шаблона помните, что все классы, которые наследуются от unittest.TestCase, выполняются как тесты. Класс Mixin в приведенном выше примере не содержит данных и поэтому не может выполняться сам по себе, поэтому он не наследуется от unittest.TestCase.

См. также

Разработка, управляемая тестированием

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

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

Пакет test может быть запущен как сценарий для управления набором регрессионных тестов Python благодаря параметру -m: python -m test. Внутри он использует test.regrtest; вызов python -m test.regrtest, используемый в предыдущих версиях Python, по-прежнему работает. Запуск сценария самостоятельно автоматически запускает все регрессионные тесты в пакете test. Он делает это, находя все модули в пакете, имена которых начинаются с test_, импортируя их и выполняя функцию test_main() при наличии или загружая тесты через unittest.TestLoader.loadTestsFromModule, если test_main не существует. Имена тестов для выполнения также могут быть переданы в сценарий. Указание одного регрессионного теста (python -m test test_spam) минимизирует вывод и выводит только то, прошел ли тест или нет.

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

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

test.support — Утилиты для набора тестов Python

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

Примечание

test.support не является общедоступным модулем. Он документирован здесь для помощи разработчикам Python в написании тестов. API этого модуля может быть изменён без обратной совместимости между выпусками.

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

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.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=None)

Вызывает ResourceDenied если resource недоступен. msg — аргумент для ResourceDenied, если он вызван. Всегда возвращает True при вызове функцией, __name__ которой '__main__'. Используется, когда тесты выполняются test.regrtest.

test.support.findfile(filename, subdir=None)

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

Установка subdir указывает относительный путь для поиска файла, а не прямой поиск в каталогах пути.

test.support.run_unittest(*classes)

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

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

def test_main():
    support.run_unittest(__name__)

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

test.support.run_doctest(module, verbosity=None)

Выполняет doctest.testmod() для данного module. Возвращает (failure_count, test_count).

Если verbosity — None, doctest.testmod() выполняется с уровнем подробности, установленным в verbose. В противном случае — с уровнем подробности None.

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 объекта. Для удобства атрибуты объекта, представляющего последнее предупреждение, также доступны непосредственно через объект 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

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

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

test.support.captured_stdin()
test.support.captured_stdout()
test.support.captured_stderr()

Контекстные менеджеры, временно заменяющие указанный поток объектом io.StringIO.

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

with captured_stdout() as stdout, captured_stderr() as stderr:
    print("hello")
    print("error", file=sys.stderr)
assert stdout.getvalue() == "hello\n"
assert stderr.getvalue() == "error\n"

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

with captured_stdin() as stdin:
    stdin.write('hello\n')
    stdin.seek(0)
    # call test code that consumes from sys.stdin
    captured = input()
self.assertEqual(captured, "hello")
test.support.temp_dir(path=None, quiet=False)

Контекстный менеджер, который создаёт временную директорию в path и возвращает директорию.

Если path — None, временная директория создаётся с помощью tempfile.mkdtemp(). Если quiet — False, менеджер контекста вызывает исключение при ошибке. В противном случае, если path указан и не может быть создан, выводится только предупреждение.

test.support.change_cwd(path, quiet=False)

Контекстный менеджер, который временно меняет текущую рабочую директорию на path и возвращает директорию.

Если quiet — False, менеджер контекста вызывает исключение при ошибке. В противном случае, он выводит только предупреждение и сохраняет текущую рабочую директорию.

test.support.temp_cwd(name='tempcwd', quiet=False)

Контекстный менеджер, который временно создаёт новую директорию и меняет текущую рабочую директорию (CWD).

Менеджер контекста создаёт временную директорию в текущей директории с именем name перед временным изменением текущей рабочей директории. Если name — None, временная директория создаётся с помощью tempfile.mkdtemp().

Если quiet — False и невозможно создать или изменить CWD, генерируется ошибка. В противном случае генерируется только предупреждение, и используется исходная CWD.

test.support.temp_umask(umask)

Контекстный менеджер, который временно устанавливает umask процесса.

test.support.can_symlink()

Возвращает True если ОС поддерживает символические ссылки, False в противном случае.

@test.support.skip_unless_symlink

Декоратор для запуска тестов, требующих поддержки символических ссылок.

@test.support.anticipate_failure(condition)

Декоратор для условной маркировки тестов с unittest.expectedFailure(). Любое использование этого декоратора должно сопровождаться комментарием, идентифицирующим соответствующую проблему отслеживания.

@test.support.run_with_locale(catstr, *locales)

Декоратор для выполнения функции в другом локали, правильно сбрасывая его после завершения. catstr — категория локали в виде строки (например "LC_ALL"). Передаваемые locales будут пробоваться последовательно, и первое допустимое локали будет использовано.

test.support.make_bad_fd()

Создаёт недопустимый дескриптор файла, открывая и закрывая временный файл и возвращая его дескриптор.

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

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

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

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

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

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

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

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

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

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

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

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

# 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'])

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

test.support.bind_port(sock, host=HOST)

Связывает сокет с свободным портом и возвращает номер порта. Использует временные порты, чтобы убедиться, что мы используем несвязанный порт. Это важно, так как многие тесты могут выполняться одновременно, особенно в среде buildbot. Этот метод вызывает исключение, если sock.family равен AF_INET, а sock.type равен SOCK_STREAM, и на сокете установлены SO_REUSEADDR или SO_REUSEPORT. Тесты никогда не должны устанавливать эти параметры сокета для TCP/IP-сокетов. Единственный случай установки этих параметров — тестирование многоадресной рассылки через несколько UDP-сокетов.

Кроме того, если параметр сокета SO_EXCLUSIVEADDRUSE доступен (например, в Windows), он будет установлен на сокете. Это предотвратит возможность связывания с нашим хостом/портом для других во время выполнения теста.

test.support.find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM)

Возвращает неиспользуемый порт, который должен быть подходящим для связывания. Это достигается путём создания временного сокета с той же семейной и типом, что и sock параметр (по умолчанию AF_INET, SOCK_STREAM), и связыванием его со указанным адресом хоста (по умолчанию 0.0.0.0) с портом, установленным на 0, для получения неиспользуемого временного порта от ОС. Временный сокет затем закрывается и удаляется, и возвращается временный порт.

Для любых тестов, где серверный сокет должен быть привязан к определённому порту на время выполнения теста, следует использовать либо этот метод, либо bind_port(). Какой из них использовать, зависит от того, создаёт ли вызывающий код сокет Python, или требуется указать неиспользуемый порт в конструкторе или передать его внешней программе (т.е. аргумент -accept в режиме s_server openssl). Всегда отдавайте предпочтение bind_port() перед find_unused_port(), когда это возможно. Использование жёстко заданного порта не рекомендуется, так как это может затруднить одновременное выполнение нескольких экземпляров теста, что представляет проблему для buildbot.

test.support.load_package_tests(pkg_dir, loader, standard_tests, pattern)

Общая реализация протокола load_tests модуля unittest для использования в тестовых пакетах. pkg_dir — это корневой каталог пакета; loader, standard_tests и pattern — это ожидаемые аргументы для load_tests. В простых случаях, __init__.py тестового пакета может быть следующим:

import os
from test.support import load_package_tests

def load_tests(*args):
    return load_package_tests(os.path.dirname(__file__), *args)
test.support.detect_api_mismatch(ref_api, other_api, *, ignore=())

Возвращает множество атрибутов, функций или методов ref_api, которых нет в other_api, за исключением определённого списка элементов, которые необходимо игнорировать в этой проверке, указанного в ignore.

По умолчанию это пропускает приватные атрибуты, начинающиеся с «_», но включает все магические методы, т.е. те, которые начинаются и заканчиваются «__».

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

test.support.check__all__(test_case, module, name_of_module=None, extra=(), blacklist=())

Проверяет, что переменная __all__ модуля module содержит все имена общедоступных элементов.

Имена общедоступных элементов (API) модуля автоматически определяются на основе соответствия соглашениям о именах общедоступных элементов и были определены в module.

Аргумент name_of_module может указывать (в виде строки или кортежа), в каких модулях мог быть определён API для его обнаружения как общедоступного API. Один из случаев этого — когда module импортирует часть своего общедоступного API из других модулей, возможно, из C-бекенда (например, csv и его _csv).

Аргумент extra может содержать набор имён, которые в противном случае не были бы автоматически обнаружены как «общедоступные», например объекты без атрибута __module__. Если он предоставлен, он будет добавлен к автоматически обнаруженным.

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

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

import bar
import foo
import unittest
from test import support

class MiscTestCase(unittest.TestCase):
    def test__all__(self):
        support.check__all__(self, foo)

class OtherTestCase(unittest.TestCase):
    def test__all__(self):
        extra = {'BAR_CONST', 'FOO_CONST'}
        blacklist = {'baz'}  # Undocumented name.
        # bar imports part of its API from _bar.
        support.check__all__(self, bar, ('bar', '_bar'),
                             extra=extra, blacklist=blacklist)

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

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

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

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

class test.support.EnvironmentVarGuard

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

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

EnvironmentVarGuard.set(envvar, value)

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

EnvironmentVarGuard.unset(envvar)

Временно удаляет переменную среды envvar.

class test.support.SuppressCrashReport

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

В Windows он отключает диалоговые окна Windows Error Reporting с помощью SetErrorMode.

В UNIX используется resource.setrlimit() для установки мягкого ограничения resource.RLIMIT_CORE в 0 для предотвращения создания файла coredump.

На обеих платформах старое значение восстанавливается методом __exit__().

class test.support.WarningsRecorder

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

class test.support.FakePath(path)

Простой объект пути. Он реализует метод __fspath__() который просто возвращает аргумент path. Если path является исключением, оно будет поднято в __fspath__().

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

Spec-Zone.ru

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