通过修补烧瓶请求对 Python 包中的装饰器进行单元测试



我有一个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.

我应该怎么做?

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

@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

最新更新