首页 > 解决方案 > Flask 测试 - 从蓝图动态测试所有受保护的路由

问题描述

我想测试来自蓝图的所有路由都受登录所需装饰器的保护。

重点是:如果开发人员添加了一条新路由并忘记添加此装饰器,我希望我的测试能够自动发现该缺陷。

为了做到这一点,我想循环遍历所有路线和方法

for rule in app.url_map.iter_rules():
    if rule.endpoint.startswith("my_blueprint"):
        response = app.test_client().get(rule)
        assert response.status_code == 401

如您所见,我必须像这样指定方法(get、post..)app.test_client().get(rule)

有没有更动态的方式来调用这些方法?

标签: pythonpython-3.xunit-testingflaskflask-testing

解决方案


发现功能

def blueprint_site_map(app, blueprint, all_methods=False):
    '''
    utilizes Flask's built-in rule mapper to generate a
    site-map of the application, returning a list of dicts, ex.
    {   
        'endpoint'  :   repr(Blueprint)
        'methods'   :   list
        'rule'      :   /route
    {
    '''
    reply = []
    rules = list(app.url_map.iter_rules())
    ignored_methods = set(() if all_methods else ('HEAD', 'OPTIONS'))
    rule_methods = [','.join(sorted(rule.methods - ignored_methods)) for rule in rules]
    for rule, methods in zip(rules, rule_methods):
        if (rule.endpoint != 'static') and (rule.endpoint.startswith(blueprint)):
            reply.append(dict(endpoint=rule.endpoint, methods=methods.split(','), rule=rule.rule))
    return reply

样本输出

>>> blueprint_site_map(app, 'my_blueprint')
[
  {
    'endpoint': 'my_blueprint.foo',
    'methods': ['GET', 'POST'],
    'rule': '/auth/foo'
  },
  {
    'endpoint': 'my_blueprint.bar',
    'methods': ['DELETE', 'GET', 'POST'],
    'rule': '/auth/bar'
  }
]

用法

def test_my_blueprint_is_protected(client):
    from flask import current_app as app
    obj = blueprint_site_map(app, 'my_blueprint')
    for each in obj:
        for method in each['methods']:
            func = getattr(client, method)
            url = each['rule']  # *see note
            kwargs = {}         # inject headers, etc if needed
            response = func(url, **kwargs)
            assert response.status_code == 401

应该注意的是,如果您使用任何参数化的 URL 规则,例如允许两者/foo/foo/<string:s>那么您将需要手动模板化或过滤掉这些规则。该blueprint_site_map函数将包含单独的列表元素/foo/foo/<string:s>,从字面上看会导致测试客户端本身或您的路由逻辑出现问题。

发现功能的设计方式是,您可以根据需要将此约定用于尽可能多的不同蓝图,就您使用蓝图约定的本质而言,这意味着您可以保持单元测试与应用程序一样模块化。

干杯!


推荐阅读