Reputation: 2058
py.test seems to fail when I decorate test functions which has a fixture as an argument.
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
In this simple example, I get the following error:
TypeError: test_something() takes exactly 1 argument (0 given).
Is there a way to fix this, preferably without modifying the decorator too much? (Since the decorator is used outside testing code too.)
Upvotes: 56
Views: 28943
Reputation: 3930
In the case where you are using a pytest fixture with a decorator while passing data to the decorator, you can do it as follows:
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))
Note how data
is passed to the wrapper
function - this way pytest's fixture approach still works correctly.
This is quite handy for example if you need to pass a PySpark session around, while using a decorator to perform other functionality.
Upvotes: 1
Reputation: 2058
It looks like functools.wraps does not do the job well enough, so it breaks py.test's introspection.
Creating the decorator using the decorator package seems to do the trick.
import decorator
def deco(func):
def wrapper(func, *args, **kwargs):
return func(*args, **kwargs)
return decorator.decorator(wrapper, func)
Upvotes: 54
Reputation: 369394
Fixture feature depends on test function signature.
If you can change wrapper signature as follow, it will works.
def deco(func):
@functools.wraps(func)
def wrapper(x):
return func(x)
return wrapper
If you can't change it, make another decorator:
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
And decorate test_somthing
with deco_x
:
@deco_x
@deco
def test_something(x):
assert x == 0
Upvotes: 10