test — Пакет регрессионных тестов для Python
Примечание
Пакет test предназначен для внутреннего использования только в Python. Он документирован для удобства основных разработчиков Python. Любое использование этого пакета за пределами стандартной библиотеки Python не рекомендуется, так как упомянутый здесь код может быть изменён или удалён без предварительного уведомления между выпусками Python.
Пакет test содержит все регрессионные тесты для Python, а также модули test.support и test.regrtest. test.support используется для улучшения ваших тестов, в то время как test.regrtest управляет набором тестов.
Каждый модуль в пакете test, имя которого начинается с test_, является набором тестов для определенного модуля или функции. Все новые тесты должны быть написаны с использованием модуля unittest или doctest. Некоторые старые тесты написаны с использованием «традиционного» стиля тестирования, который сравнивает вывод, напечатанный в sys.stdout; этот стиль тестирования считается устаревшим.
См. также
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, или через командную строку python -m unittest.
Цель регрессионного тестирования — попытаться сломать код. Это приводит к нескольким рекомендациям:
- Набор тестов должен проверять все классы, функции и константы. Это относится не только к внешнему 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.
См. также
- Разработка, управляемая тестированием
- Книга Кента Бека о написании тестов до написания кода.
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()выполняется с параметром verbosity, установленным вverbose. В противном случае он выполняется с verbosity, установленным в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()доступен через атрибутwarningsобъекта записывателя. Для удобства, атрибуты объекта, представляющего последнее предупреждение, также можно получить непосредственно через объект записывателя (см. пример ниже). Если не было поднято никакого предупреждения, то любые атрибуты, которые в противном случае ожидались бы у объекта, представляющего предупреждение, вернутNone.Объект записывателя также имеет метод
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(). Если quietFalse, менеджер контекста вызывает исключение при ошибке. В противном случае, если 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.
-
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()по возможности. Использование жёстко заданного порта не рекомендуется, так как это может сделать невозможным одновременное выполнение нескольких экземпляров теста, что является проблемой для buildbots.
-
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 определены следующие классы:
-
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, используя SetErrorMode.
В UNIX используется
resource.setrlimit()для установки мягкого пределаresource.RLIMIT_COREв 0, чтобы предотвратить создание файлов coredump.На обеих платформах старое значение восстанавливается с помощью
__exit__().
-
class test.support.WarningsRecorder -
Класс, используемый для записи предупреждений для юнит-тестов. Подробнее см. документацию к
check_warnings()выше.
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.5/library/test.html