python通过模拟测试装饰的装饰

时间:2012-11-13 11:10:05

标签: python testing mocking decorator

这个问题是对Python中装饰器this brilliant answer的后续跟进:

我使用给定的“代码片段使任何装饰器一般接受任何参数”。

然后我有这个(这里是简化的)装饰者:

@decorator_with_args
def has_permission_from_kwarg(func, *args, **kwargs):
    """Decorator to check simple access/view rights by the kwarg."""
    def wrapper(*args_1, **kwargs_1):
        if 'kwarg' in kwargs_1:
            kwarg = kwargs_1['kwarg']
        else:
            raise HTTP403Error()

        return func(*args_1, **kwargs_1)

    return wrapper
  1. 使用这个装饰器,没问题,它能很好地完成这项工作。
  2. 测试一个不需要绝对kwargs的相似装饰器,结果相同。
  3. 但是使用以下模拟测试此装饰器不起作用:

    def test_can_access_kwarg(self):
        """Test simple permission decorator."""
        func = Mock(return_value='OK')
        decorated_func = has_permission_from_slug()(func(kwarg=self.kwarg))
        # It will raise at the following line, whereas the kwarg is provided...
        response = decorated_func()
        self.assertTrue(func.called)
        self.assertEqual(response, 'OK')
    
  4. 当我没有'kwarg'关键字参数时,它会返回我正在提出的异常......

    有没有人知道如何测试(通过模拟会更好)这样的装饰器由另一个装饰器装饰,需要访问传递给函数的一个关键字参数?

1 个答案:

答案 0 :(得分:2)

decorated_func = has_permission_from_slug()(func(kwarg=self.kwarg))

这将:

  1. 执行func(kwarg=self.kwarg)
  2. 生成实际装饰器的实例。
  3. 根据func- 调用的结果调用装饰器(即结果)。
  4. 返回包装器,稍后将尝试调用步骤3中的结果(这将失败)。

    response = decorated_func()

  5. 然后,这将调用返回的包装器,不带参数,因此**kwargs_1为空。此外,如果您在这种情况下包装器不会引发异常,则后续调用func(..)会抛出异常,因为func(原始的)的返回值可能无法调用(参见上文) )。

    你可能想要做的事情,或者至少你的装饰者支持的是:

    decorated_func = has_permission_from_kwarg()(func)
    response = decorated_func(kwarg=self.kwarg)
    

    或者,如果您想在装饰器中传递kwarg,请执行以下操作:

    decorated_func = has_permission_from_kwarg(kwarg=self.kwarg)(func)
    response = decorated_func()
    

    然后你需要调整或装饰来实际使用kwargs,而不是kwargs_1在检查中(后者是装饰函数的参数)。


    我正在使用以下代码测试您的原始装饰器定义(没有更改)和链接答案中定义的decorator_with_args

    class HTTP403Error (Exception):
        pass
    
    def func (*args, **kwargs):
        print('func {}; {}'.format(args, kwargs))
    
    my_kwarg = 'foo'
    decorated_func = has_permission_from_kwarg()(func)
    decorated_func(kwarg=my_kwarg)
    decorated_func(not_kwarg=my_kwarg)
    

    正如预期的那样,我为第一次调用打印func (); {'kwarg': 'foo'},为第二次调用打印HTTP403异常。