Автоматически оберните / украсьте все модульные тесты pytest

#python #testing #pytest #fixtures #monkeypatching

Вопрос:

Допустим, у меня есть очень простой декоратор для ведения журнала:

 from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"{func.__name__} ran with args: {args}, and kwargs: {kwargs}")
        result = func(*args, **kwargs)
        return result
    return wrapper
 

Я могу добавить этот декоратор в каждый модульный тест pytest индивидуально:

 @my_decorator
def test_one():
    assert True

@my_decorator
def test_two():
    assert 1
 

Как я могу автоматически добавлять этот декоратор в каждый модульный тест pytest, чтобы мне не приходилось добавлять его вручную? Что делать, если я хочу добавить его в каждый модульный тест в файле? Или в модуле?

Мой вариант использования-обернуть каждую тестовую функцию профилировщиком SQL, поэтому неэффективный код ORM вызывает ошибку. Использование прибора pytest должно сработать, но у меня есть тысячи тестов, поэтому было бы неплохо автоматически применять оболочку вместо добавления прибора в каждый отдельный тест. Кроме того, может быть один или два модуля, которые я не хочу профилировать, поэтому было бы полезно иметь возможность отказаться или отказаться от всего файла или модуля.

Комментарии:

1. Можете ли вы предоставить некоторую информацию о вашем случае использования? Стандартным способом в pytest было бы использование автоматического приспособления, хотя, может быть, вы хотите сделать что-то, что невозможно сделать с помощью приспособления?

2. @MrBeanBremen только что обновил вопрос выше с моим вариантом использования

Ответ №1:

При условии, что вы можете переместить логику в приспособление, как указано в вопросе, вы можете просто использовать приспособление автоматического использования, определенное на верхнем уровне conftest.py .

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

conftest.py

 import pytest

def pytest_configure(config):
    config.addinivalue_line(
        "markers",
        "no_profiling: mark test to not use sql profiling"
    )

@pytest.fixture(autouse=True)
def sql_profiling(request):
    if not request.node.get_closest_marker("no_profiling"):
        # do the profiling
    yield
 

test.py

 import pytest

def test1():
    pass # will use profiling

@pytest.mark.no_profiling
def test2():
    pass # will not use profiling
 

Как указал @hoefling, вы также можете отключить приспособление для всего модуля, добавив:

 pytestmark = pytest.mark.no_profiling
 

в модуле. Это добавит маркер ко всем содержащимся тестам.

Комментарии:

1. Хороший ответ — это также позволяет отключить светильник для всего модуля, добавив параметр модуля pytestmark = pytest.mark.no_profiling . Таким образом, никакой декоратор на заказ вообще не должен быть нужен!

2. Я получил следующую ошибку: ValueError: unknown configuration value: 'no_profiling' — но после того, как я удалил pytest_configure , все работало идеально. Спасибо!

3. Ах, извините — я допустил ошибку в синтаксисе регистрации маркеров, исправил ее сейчас. Без этого вы получите предупреждение о неизвестном маркере.