How do I make pytest fixtures work with decorated functions?

15,215

Solution 1

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)

Solution 2

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
Share:
15,215
jck
Author by

jck

Updated on June 25, 2022

Comments

  • jck
    jck about 2 years

    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.)

  • zneak
    zneak over 6 years
    It could be useful to know that pytest depends on decorator, so you don't need to pull in any new dependency.
  • falsetru
    falsetru over 6 years
    @KjeldFlarup, How about jck's answer ?
  • Steven
    Steven over 5 years
    Awesome! Great job
  • user1065000
    user1065000 about 4 years
    What to do if I want to pass an argument to decorator itself. e.g. @deco_x(2)
  • user1065000
    user1065000 about 4 years
    How shall we handle passing an argument to deco when using this e.g. @deco(3)
  • joel
    joel about 4 years
    why are there two uses of func in your answer? is that intentional?
  • paveldroo
    paveldroo almost 3 years
    @joel it seems intentional. Without second func decorator doesn't work, though even PyCharm warns about shadowing name from outer scope.