为 pytest 测试方法编写装饰器

5 python decorator pytest

假设以下结构:

class SetupTestParam(object):
    def setup_method(self, method):
        self.foo = bar()

    @pytest.fixture
    def some_fixture():
        self.baz = 'foobar'
Run Code Online (Sandbox Code Playgroud)

SetupTestParam用作测试类的父类。

class TestSomething(SetupTestParam):
    def test_a_lot(self, some_fixture):
        with self.baz as magic:
            with magic.fooz as more_magic:
                 blah = more_magic.much_more_magic() # repetative bleh
            ... # not repetative code here
            assert spam == 'something cool'
Run Code Online (Sandbox Code Playgroud)

现在,编写测试变得重复(使用语句),我想编写一个装饰器来减少代码行数。但是pytest和函数签名有问题。

我发现了应该有帮助的图书馆,但我无法让它工作。

classmethodSetupTestParam课堂上做了一个。

@classmethod
@decorator.decorator
def this_is_decorator(cls, f):
    def wrapper(self, *args, **kw):
        with self.baz as magic:
            with magic.fooz as more_magic:
                 blah = more_magic.much_more_magic() # repetative bleh
            return f(self, *args)
    return wrapper
Run Code Online (Sandbox Code Playgroud)

在我装饰test_a_lot方法后,我收到错误TypeError: transaction_decorator() takes exactly 1 argument (2 given)

有人可以解释一下我做错了什么吗?(我认为self测试方法有问题?)

小智 5

经过一些调整并意识到我需要将参数传递给装饰器后,我选择将其编写为一个类:

class ThisIsDecorator(object):
    def __init__(self, param):
        self.param = param   # Parameter may vary with the function being decorated
    def __call__(self, fn):
        wraps(fn) # [1]
        def wrapper(fn, fn_self, *args): # [2] fn_self refers to original self param from function fn (test_a_lot) [2]
            with fn_self.baz as fn_self.magic: # I pass magic to fn_self to make magic accesible in function fn (test_a_lot)
                with fn_self.magic.fooz as more_magic:
                    blah = self.param.much_more_magic() # repetative bleh
            return fn(fn_self, *args)
        return decorator.decorator(wrapper, fn) 
Run Code Online (Sandbox Code Playgroud)

[1] 我以前wraps有原始的fn __name__,__module____doc__

[2] 参数传递给 wrapperwereself = <function test_a_lot at 0x24820c8> args = (<TestSomething object at 0x29c77d0>, None, None, None, None), kw = {}所以我取出了args[0]作为fn_self.

原始版本(不传递参数):

 @classmethod
 def this_is_decorator(cls, fn):
     @wraps(fn)
     def wrapper(fn, fn_self, *args):
         with fn_self.baz as fn_self.magic:
             with fn_self.magic.fooz as more_magic:
                 blah = more_magic.much_more_magic() # repetative bleh
             return fn(fn_self, *args)
     return decorator.decorator(wrapper,fn)
Run Code Online (Sandbox Code Playgroud)

感谢迈克·穆勒指出了正确的方向。