0

Как заставить фикстуры pytest работать с декорированными функциями?

14

Проблема с использованием декоратора в тестах с фикстурами в py.test

Я пытаюсь использовать декоратор для тестовой функции, которая принимает фикстуру в качестве аргумента, и сталкиваюсь с ошибкой. Вот минимальный пример кода, где я демонстрирую проблему:

import functools
import pytest

def deco(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@pytest.fixture
def x():
    return 0

@deco
def test_something(x):
    assert x == 0

При запуске тестов я получаю следующую ошибку:

TypeError: test_something() takes exactly 1 argument (0 given).

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

Буду признателен за помощь!

2 ответ(ов)

0

Функция-фикстура зависит от сигнатуры тестовой функции.

Если у вас есть возможность изменить сигнатуру обертки, то это будет работать:

def deco(func):
    @functools.wraps(func)
    def wrapper(x):
        return func(x)
    return wrapper

Если вы не можете изменить ее, создайте другой декоратор:

def deco(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

def deco_x(func):
    @functools.wraps(func)
    def wrapper(x):
        return func(x)
    return wrapper

И используйте декоратор deco_x для функции test_something:

@deco_x
@deco
def test_something(x):
    assert x == 0

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

0

Если вы используете фикстуру pytest с декоратором и необходимо передать данные в декоратор, вы можете сделать это следующим образом:

import pytest

@pytest.fixture(scope="session")
def data(request):
    data = list(range(10))
    yield data

def decorator_with_variable_and_fixture(whatever):
    def wrap(test_func):
        def wrapper(data):
            mod = whatever.lower()[::-1]
            test_func(data, mod)
        return wrapper
    return wrap

@decorator_with_variable_and_fixture(whatever='SOMETHING!')
def test_decorator_with_variable_and_fixture(data, whatever):
    for i in data:
        print(whatever[i]*(i+1))
    assert whatever == '!gnihtemos'
    assert data == list(range(10))

Обратите внимание, как data передается в функцию wrapper — таким образом, подход с фиктурами pytest работает корректно.

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь