【问题标题】:unit test for decorator in a python package by patching flask request通过修补烧瓶请求对python包中的装饰器进行单元测试
【发布时间】:2017-03-20 07:52:46
【问题描述】:

我有一个 python 模块security.py,它定义了一个装饰器authorized()

我想测试装饰器。装饰器将收到一个烧瓶请求标头。 装饰器是这样的:

def authorized():
    def _authorized(wrapped_func):
        def _wrap(*args, **kwargs):
            if 'token' not in request.headers:
                LOG.warning("warning")
                abort(401)
                return None
            return wrapped_func(*args, **kwargs)
        return _wrap
    return _authorized

我想使用@patch 装饰器模拟烧瓶请求头。我写的测试是这样的:

@patch('security.request.headers', Mock(side_effect=lambda *args, **kwargs: MockHeaders({})))
def test_no_authorization_token_in_header(self):
    @security.authorized()
    def decorated_func(token='abc'):
        return access_token

    result = decorated_func()
    self.assertEqual(result, None)

class MockHeaders(object):
    def __init__(self, json_data):
        self.json_data=json_data

但我总是收到以下错误:

name = 'request'

def _lookup_req_object(name):
    top = _request_ctx_stack.top
    if top is None:
       raise RuntimeError(_request_ctx_err_msg)

       RuntimeError: Working outside of request context.

       This typically means that you attempted to use functionality that needed
       an active HTTP request.  Consult the documentation on testing for
       information about how to avoid this problem.

我应该怎么做才对?

【问题讨论】:

  • @MartijnPieters 是的,该类就在补丁修饰的方法之后
  • 糟糕,错过了。过错。

标签: python unit-testing flask


【解决方案1】:

模拟整个请求对象以避免触发上下文查找:

@patch('security.request')

并从那里建立模拟:

@patch('security.request')
def test_no_authorization_token_in_header(self, mock_request):
    mock_request.headers= {}

    @security.authorized()
    def decorated_func(token='abc'):
        return token

    self.assertRaises(Abort):
        result = decorated_func()

由于缺少令牌会导致引发Abort 异常,因此您应该对此进行显式测试。请注意request.headers 属性不会在任何地方调用,因此side_effectreturn_value 属性不适用于此处。

我完全忽略了MockHeaders;您的装饰器没有使用 json_data 并且您的实现缺少 __contains__ 方法,因此 in 测试无法解决此问题。对于当前的被测代码,一个普通的字典就足够了。

旁注:authorized 是一个装饰器工厂,但它不带任何参数。如果您根本不使用那里的工厂,那就更清楚了。您还应该使用functools.wraps() 来确保其他装饰器添加的任何元数据都被正确传播:

from functools import wraps

def authorized(wrapped_func):
    @wraps(wrapped_func)
    def _wrap(*args, **kwargs):
        if 'token' not in request.headers:
            LOG.warning("warning")
            abort(401)
            return None
        return wrapped_func(*args, **kwargs)
    return _wrap

然后直接使用装饰器(所以没有调用):

@security.authorized
def decorated_func(token='abc'):
    return access_token

【讨论】:

    猜你喜欢
    • 2021-11-25
    • 1970-01-01
    • 2012-07-14
    • 2017-07-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-12
    • 2017-05-03
    • 2012-04-26
    相关资源
    最近更新 更多