【问题标题】:python testing a decorated decorator through a mockpython通过模拟测试装饰的装饰器
【发布时间】:2012-11-01 19:43:36
【问题描述】:

这个问题是this brilliant answer 在 Python 中的装饰器上的后续问题:

我使用给定的“sn-p 使任何装饰器通常接受任何参数”。

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

@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')
    

当我没有“kwarg”关键字参数时,它会返回我提出的异常...

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

【问题讨论】:

    标签: python testing mocking decorator


    【解决方案1】:
    decorated_func = has_permission_from_slug()(func(kwarg=self.kwarg))
    

    这将:

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

      response = decorated_func()

    这将调用返回的不带参数的包装器,因此**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 异常。

    【讨论】:

    • 嗨戳,感谢您的回答!我应该之前说过,但我肯定也尝试过你提出的建议。不幸的是:第一个示例失败,因为 response = decorated_func(kwarg=self.kwarg) 返回“TypeError: 'str' object is not callable” 第二个有效,但不适合我的原始范围,因为我想测试一个在我的代码中使用 kwargs_1 工作的装饰器.
    • 在上一个代码示例中有一个小错误,现在也可以了。不过,第一个解决方案之前有效。
    • 很好,但第二个例子仍然不符合我的目的。第一个仍然失败。它完成了超越 kwarg 检查的工作,但在 return func(*args_1, **kwargs_1) 行出现 TypeError 失败
    • 我的错误,实际上在通过提供参数 func(kwarg=self.kwarg) 测试了调用之后,我让它像 decorated_func = has_permission_from_kwarg()(func()) 一样,作为一个名为 !!!非常遗憾。最后,同样使用 Mock,您发布的第一个代码 sn-p 工作。感谢您的帮助戳!
    猜你喜欢
    • 2011-02-13
    • 2016-08-20
    • 2017-08-20
    • 2022-06-14
    • 1970-01-01
    • 1970-01-01
    • 2021-06-04
    • 2023-03-18
    • 1970-01-01
    相关资源
    最近更新 更多