Исторические заметки

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

Реконструкция и итерация маркеров

Изменено в версии 3.6.

Реализация маркеров в pytest традиционно работала путем простого обновления атрибута __dict__ функций для кумулятивного добавления маркеров. В результате маркеры непреднамеренно передавались по иерархии классов неожиданным образом. Кроме того, API для их извлечения был непоследовательным, поскольку маркеры, полученные в результате параметризации, хранились иначе, чем маркеры, применяемые с помощью декоратора @pytest.mark и маркеры, добавляемые с помощью node.add_marker.

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

В зависимости от того, как маркер был объявлен/изменен, можно получить либо MarkerInfo, который может содержать маркеры из классов-сестер, MarkDecorators, когда метки приходят из параметризации или из вызова node.add_marker, отбрасывая предыдущие метки. Также MarkerInfo действует как одна метка, когда на самом деле она представляет собой объединенное представление нескольких меток с одним и тем же именем.

Кроме того, маркеры не были доступны одинаково для модулей, классов и функций/методов. Фактически, маркеры были доступны только в функциях, даже если они были объявлены в классах/модулях.

Новый API для доступа к маркерам был представлен в pytest 3.6 для того, чтобы решить проблемы с первоначальным дизайном, предоставив метод _pytest.nodes.Node.iter_markers() для последовательного итерационного просмотра маркеров и переработав внутренние компоненты, что решило большое количество проблем с первоначальным дизайном.

Обновление кода

Старая функция Node.get_marker(name) считается устаревшей, поскольку она возвращает внутренний объект MarkerInfo, который содержит объединенное имя, *args и **kwargs всех маркеров, которые применяются к данному узлу.

В общем, существует два сценария того, как следует обращаться с маркерами:

1. Marks overwrite each other. Order matters but you only want to think of your mark as a single item. E.g. log_level('info') at a module level can be overwritten by log_level('debug') for a specific test.

В этом случае используйте Node.get_closest_marker(name):

# replace this:
marker = item.get_marker("log_level")
if marker:
    level = marker.args[0]

# by this:
marker = item.get_closest_marker("log_level")
if marker:
    level = marker.args[0]

2. Marks compose in an additive manner. E.g. skipif(condition) marks mean you just want to evaluate all of them, order doesn’t even matter. You probably want to think of your marks as a set here.

В этом случае выполните итерацию по каждой метке и обработайте их *args и **kwargs по отдельности.

# replace this
skipif = item.get_marker("skipif")
if skipif:
    for condition in skipif.args:
        # eval condition
        ...

# by this:
for skipif in item.iter_markers("skipif"):
    condition = skipif.args[0]
    # eval condition

Если вы не уверены или у вас есть вопросы, пожалуйста, откройте an issue.

плагин кэша, интегрированный в ядро

Функциональность плагина core cache ранее распространялась как сторонний плагин под названием pytest-cache. Ядро плагина совместимо в отношении опций командной строки и использования API, за исключением того, что вы можете хранить/получать данные между тестовыми запусками только в json-сериализуемом виде.

фанкарги и pytest_funcarg__.

В версиях до 2.3 не было маркера @pytest.fixture, и вам приходилось использовать магический префикс pytest_funcarg__NAME для фабрики приспособлений. Это остается и будет поддерживаться, но больше не рекламируется как основное средство объявления функций приспособления.

@pytest.yield_fixture декоратор

До версии 2.10, чтобы использовать оператор yield для выполнения кода разрушения, необходимо было пометить фикстуру с помощью маркера yield_fixture. Начиная с версии 2.10, обычные фикстуры могут использовать yield напрямую, поэтому декоратор yield_fixture больше не нужен и считается устаревшим.

[pytest] заголовок в setup.cfg

До версии 3.0 поддерживаемое имя секции было [pytest]. Из-за того, что это может противоречить некоторым командам distutils, рекомендуемым именем секции для файлов setup.cfg теперь является [tool:pytest].

Обратите внимание, что для файлов pytest.ini и tox.ini имя секции [pytest].

Применение меток к параметрам @pytest.mark.parametrize

До версии 3.1 поддерживаемый механизм для маркировки значений использовал синтаксис:

import pytest


@pytest.mark.parametrize(
    "test_input,expected", [("3+5", 8), ("2+4", 6), pytest.mark.xfail(("6*9", 42))]
)
def test_eval(test_input, expected):
    assert eval(test_input) == expected

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

Старый синтаксис планируется убрать в pytest-4.0.

@pytest.mark.parametrize имена аргументов в виде кортежа

В версиях до 2.4 имена аргументов нужно было указывать в виде кортежа. Это остается актуальным, но более простой синтаксис "name1,name2,..." с разделенными запятыми строками теперь рекламируется в первую очередь, потому что он проще в написании и производит меньше шума в строках.

setup: теперь является «приспособлением для автодоводки».

Во время разработки до выпуска pytest-2.3 использовалось имя pytest.setup, но перед выпуском оно было переименовано и перенесено в общий механизм фиксации, а именно Автоматические светильники (светильники, которые не нужно запрашивать).

Условия в виде строк вместо булевых значений

До pytest-2.4 единственным способом указать условия skipif/xfail было использование строк:

import sys


@pytest.mark.skipif("sys.version_info >= (3,3)")
def test_function():
    ...

Во время установки тестовой функции условие skipif оценивается вызовом eval('sys.version_info >= (3,0)', namespace). Пространство имен содержит все глобальные файлы модуля, а также os и sys как минимум.

Начиная с pytest-2.4 boolean conditions считаются предпочтительными, поскольку маркеры можно свободно импортировать между модулями тестирования. При использовании строк необходимо импортировать не только маркер, но и все переменные, используемые маркером, что нарушает инкапсуляцию.

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

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

Оценка строки условия в pytest.mark.skipif(conditionstring) или pytest.mark.xfail(conditionstring) происходит в словаре пространства имен, который строится следующим образом:

  • пространство имен инициализируется путем помещения в него модулей sys и os и объекта pytest config.

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

Объект pytest config позволяет вам пропускать на основе значения конфигурации теста, которое вы могли добавить:

@pytest.mark.skipif("not config.getvalue('db')")
def test_function():
    ...

Эквивалент с «булевыми условиями»:

@pytest.mark.skipif(not pytest.config.getvalue("db"), reason="--db was not specified")
def test_function():
    pass

Примечание

Вы не можете использовать pytest.config.getvalue() в коде, импортированном до того, как произойдет разбор аргументов pytest. Например, файлы conftest.py импортируются до разбора командной строки, и поэтому config.getvalue() не будет выполняться правильно.

pytest.set_trace()

До версии 2.4 для установки точки останова в коде нужно было использовать pytest.set_trace():

import pytest


def test_function():
    ...
    pytest.set_trace()  # invoke PDB debugger and tracing

В этом больше нет необходимости, и можно напрямую использовать нативный вызов import pdb;pdb.set_trace().

Для получения более подробной информации смотрите Установка точек останова.

свойства «compat»

Доступ к экземплярам Module, Function, Class, Instance, File и Item через Node уже давно документирован как устаревший, но начал выдавать предупреждения начиная с pytest 3.9 и далее.

Пользователи должны просто import pytest и обращаться к этим объектам с помощью модуля pytest.

Вернуться на верх