Фреймворк pytest
создает пользовательские метки с аргументами динамически с использованием фабричного объекта pytest.mark
. Затем созданные метки применяются к функциям в качестве декоратора. Аргументы пользовательских меток читаются фикстурой тестовой функции и могут передаваться в саму тестовую функцию.
Например:
@pytest.mark.timeout(10, "slow", method="thread") def test_function(): ...
Этот код создаст и прикрепит объект Mark
к собранному элементу, к которому затем можно будет получить доступ с помощью объекта фикстуры request
через хук pytest.node.iter_markers()
. Объект метки mark
будет иметь следующие атрибуты:
mark.name == "timeout" mark.args == (10, "slow") mark.kwargs == {"method": "thread"}
Пример использования нескольких пользовательских меток:
@pytest.mark.timeout(10, "slow", method="thread") @pytest.mark.slow def test_function(): ...
Полный пример извлечения аргументов из пользовательских меток.
Когда итератор request.node.iter_markers
или for ... in request.node.iter_markers_with_node(name=None)
используются с несколькими декораторами пользовательских меток, то маркер, ближайший к функции, будет итерироваться первым. Приведенный выше пример приведет к @pytest.mark.slow
, за которым следует @pytest.mark.timeout(...)
.
Чтобы зарегистрировать пользовательскую метку, необходимо ее название просто добавить в файл pytest.ini
, который расположен в корне проекта:
# pytest.ini [pytest] markers = fixt_data(arg1, arg2): маркер с аргументами. slow: медленный тест.
Внимание! Не используйте в названиях маркеров ключевые слова языка Python.
Незарегистрированные метки всегда будут выдавать предупреждение warning
. Можно отключить предупреждение для пользовательских меток, зарегистрировав их программно, используя хук pytest_configure()
.
def pytest_configure(config): # регистрируется метка с параметрами `@pytest.mark.fixt_data(data)` config.addinivalue_line("markers", "fixt_data(data): регистрация маркера с параметрами") # регистрируется метка `@pytest.mark.slow` config.addinivalue_line("markers", "slow: медленный тест.")
Используя объект запроса request
, фикстура также может получить доступ к пользовательским маркерам, которые применяются к тестовой функции. Такое поведение может быть полезно для передачи данных в фикстуру из теста или в тест через фикстуру:
import pytest @pytest.fixture def fixt(request): # Считываем данные, по имени маркера # примененного к тесту `test_fixt()` marker = request.node.get_closest_marker("fixt_data") if marker is None: # Каким-то образом обработать отсутствующий маркер... data = None else: data = marker.args[0] # Делаем что-то с данными и возвращаем return data # Маркер передает данные тестовой функции @pytest.mark.fixt_data(42) # функция принимает фикстуру `fixt` def test_fixt(fixt): assert fixt == 42
Другой пример: Маркеры можно взять из атрибута узла запроса фикстуры с помощью request.node.iter_markers()
:
import pytest @pytest.fixture def get_marks(request): # фикстура получает доступ к маркерам теста # `test_marks()` при помощи аргумента `request` marks = [m.name for m in request.node.iter_markers()] if request.node.parent: marks += [m.name for m in request.node.parent.iter_markers()] yield marks @pytest.mark.parametrize('number', [1, 2, 3]) @pytest.mark.foo @pytest.mark.xfail def test_marks(get_marks, number): print(get_marks) assert 'xfail' in get_marks assert number == 42