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

时间:2017-03-20 07:52:46

标签: python unit-testing flask

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

我该怎么做?

1 个答案:

答案 0 :(得分:3)

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

@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