You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对含自定义verify_jwt装饰器的Flask路由做单元测试?

Flask路由单元测试:绕过自定义JWT装饰器的正确姿势

核心问题分析

你的自定义verify_jwt装饰器和路由函数在同一模块,导入时装饰器已经完成对函数的包裹,常规补丁方式失效的原因通常是:

  • 补丁了flask_jwt_extended.verify_jwt_in_request而非模块内导入的引用
  • 导入模块后才补丁装饰器,此时函数已经被原装饰器处理过

可行解决方案

方案1:补丁装饰器内的JWT验证函数

直接替换模块内verify_jwt_in_request的实现,让它在测试中不抛出异常(或模拟异常)。

假设你的业务模块app.py代码如下:

from flask import Flask, jsonify
from flask_jwt_extended import verify_jwt_in_request
import logging

app = Flask(__name__)

def verify_jwt(f):
    def wrapper(*args, **kwargs):
        try:
            verify_jwt_in_request()
            return f(*args, **kwargs)
        except Exception as e:
            logging.error(f"JWT验证失败: {e}")
            return jsonify({"msg": "未授权"}), 401
    return wrapper

@app.route('/protected')
@verify_jwt
def protected_route():
    return jsonify({"msg": "访问成功"}), 200

测试代码(用pytest):

from app import app
import pytest

def test_valid_jwt_access(monkeypatch):
    # 替换app模块内的verify_jwt_in_request,模拟验证通过
    def mock_verify():
        pass
    monkeypatch.setattr('app.verify_jwt_in_request', mock_verify)

    client = app.test_client()
    resp = client.get('/protected')
    assert resp.status_code == 200
    assert resp.get_json() == {"msg": "访问成功"}

def test_invalid_jwt_access(monkeypatch):
    # 模拟验证失败抛出异常
    def mock_verify():
        raise Exception("无效token")
    monkeypatch.setattr('app.verify_jwt_in_request', mock_verify)

    client = app.test_client()
    resp = client.get('/protected')
    assert resp.status_code == 401
    assert resp.get_json() == {"msg": "未授权"}

方案2:绕过整个自定义装饰器

在导入业务模块前,将verify_jwt替换为无操作装饰器(直接返回原函数),彻底跳过JWT验证逻辑。

测试代码:

import sys
from unittest.mock import Mock
from flask import Flask
import pytest

def test_bypass_jwt_decorator():
    # 提前mock app模块,替换verify_jwt为无操作装饰器
    mock_verify_jwt = Mock(side_effect=lambda f: f)
    mock_app = Mock()
    mock_app.verify_jwt = mock_verify_jwt
    mock_app.app = Flask(__name__)
    
    # 将mock模块注入sys.modules,确保后续导入的是mock版本
    sys.modules['app'] = mock_app

    # 现在导入路由函数,它不会被原装饰器包裹
    from app import protected_route, app

    # 重新绑定路由(因为mock模块的路由未初始化)
    @app.route('/protected')
    def temp_route():
        return protected_route()

    client = app.test_client()
    resp = client.get('/protected')
    assert resp.status_code == 200
    assert resp.get_json() == {"msg": "访问成功"}

关键注意事项

  • 补丁路径必须是业务模块内的函数引用(如app.verify_jwt_in_request),而非原库的全局路径,因为装饰器调用的是模块内导入的本地引用。
  • 若要替换装饰器,必须在import app之前完成mock,否则函数已经被原装饰器处理,补丁无效。

内容的提问来源于stack exchange,提问作者mokhinurra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 13:31:15