如何在不使用requests的情况下调用含头部校验装饰器的Python Flask后端函数并解决单元测试请求上下文问题
解决Flask单元测试中请求上下文错误的问题
这个问题我做Flask项目时也碰到过,核心原因很明确:你的require_auth装饰器里的get_username()、unauthorized()这些操作,都依赖Flask的请求上下文才能正常工作,但直接调用视图函数做单元测试时,并没有激活这个上下文,所以才会抛出Working outside of request context的错误。
下面给你几个实用的解决思路,根据你的测试需求选就行:
1. 用Flask测试客户端调用API(最推荐)
这是最贴近真实业务场景的测试方式,Flask的测试客户端会自动帮你处理请求上下文,还能模拟HTTP请求头,完美覆盖认证逻辑。
修改你的测试代码如下:
import unittest from unittest.mock import patch from backend_api import app # 导入你的Flask app实例 class TestBackendApi(unittest.TestCase): def setUp(self): # 初始化测试客户端,开启测试模式 self.client = app.test_client() app.config['TESTING'] = True @patch("pymongo.collection.Collection.find_one", side_effect=[projects_json]) def test_get_records(self, mock_find): # 按你的认证要求添加请求头,比如Bearer Token或者cookie auth_headers = { 'Authorization': 'Bearer your_valid_test_token' } # 用测试客户端发送GET请求到API路由 response = self.client.get('/records/61729c18afe7a83268c6c9b8', headers=auth_headers) # 断言响应状态码 self.assertEqual(response.status_code, 200, "Expected 200 OK response")
2. 手动推送请求上下文
如果你就想直接调用视图函数而不是走HTTP路由,可以手动创建并推送请求上下文,把认证需要的头部信息传进去:
import unittest from unittest.mock import patch from backend_api import get_records, app class TestBackendApi(unittest.TestCase): @patch("pymongo.collection.Collection.find_one", side_effect=[projects_json]) def test_get_records(self, mock_find): # 用test_request_context创建并激活请求上下文,传入认证头 with app.test_request_context(headers={'Authorization': 'Bearer test_token'}): # 现在可以安全调用视图函数了 resp = get_records('61729c18afe7a83268c6c9b8') self.assertEqual(resp.status_code, 200, "Status code mismatch")
3. Mock认证相关的依赖函数
如果你的测试重点是API的业务逻辑(比如数据库查询是否正确),不想测试认证逻辑,可以直接mock装饰器里的关键函数,让它们返回合法值:
import unittest from unittest.mock import patch from backend_api import get_records class TestBackendApi(unittest.TestCase): @patch("pymongo.collection.Collection.find_one", side_effect=[projects_json]) @patch("commonlib.auth.get_username", return_value="test_valid_user") # mock合法用户名 @patch("commonlib.auth.is_auth_valid", return_value=True) # mock认证通过 def test_get_records(self, mock_auth_valid, mock_get_user, mock_find): # 此时装饰器会认为认证合法,直接执行业务逻辑 resp = get_records('61729c18afe7a83268c6c9b8') self.assertEqual(resp.status_code, 200, "Status code should be 200")
甚至你可以临时跳过装饰器,直接调用原函数(不过这种方式跳过了认证逻辑,适合纯业务测试):
def test_get_records(self, mock_find): from backend_api import get_records # 访问__wrapped__获取被装饰的原函数 original_function = get_records.__wrapped__ resp = original_function('61729c18afe7a83268c6c9b8') self.assertEqual(resp.status_code, 200)
4. 给装饰器加测试环境判断(可选)
如果你希望在测试环境自动跳过认证,可以修改require_auth装饰器,添加测试环境判断:
from flask import current_app def require_auth(func): """ Decorator that can be added to a function to check for authorization """ def wrapper(*args, **kwargs): # 如果是测试环境,直接执行原函数 if current_app.config.get('TESTING'): return func(*args, **kwargs) # 原认证逻辑 username = get_username() security_log = { 'loginId': username, 'securityProtocol': _get_auth_type(), } try: if username is None: raise SecurityException('Authorization header or cookie not found') if not is_auth_valid(): raise SecurityException('Authorization header or cookie is invalid') except SecurityException as ex: log_security(result='DENIED', message=str(ex)) unauthorized(str(ex)) return func(*args, **kwargs) return wrapper
然后在测试的setUp里开启测试模式:
def setUp(self): app.config['TESTING'] = True
内容的提问来源于stack exchange,提问作者Rajasekhar
相关产品推荐
相关产品推荐

