单元测试Mock Flask的request.get_json触发请求上下文外运行错误
问题:Flask单元测试中正确Mock request.get_json()的方法
我正在为大型Flask应用的后端开发单元测试,目的是验证辅助函数get_post_args()能否正确处理空请求,编写的测试代码如下:
from flask import request from unittest.mock import patch from typing import Dict, Any from errors import NoPostArguments def get_post_args() -> Dict[str, Any]: args = request.get_json() if not isinstance(args, dict): raise NoPostArguments( "no arguments given for this POST request; request not served" ) return args def test_get_post_args_returns_none(): with patch( "request.get_json", return_value=None, ): with unittest.TestCase().assertRaises(NoPostArguments): get_post_args()
使用pytest运行该测试时,出现如下错误:
test_get_post_args_returns_none failed: def test_get_post_args_returns_none(): > with patch( "flask.request.get_json", return_value=None, ): tests\unit_tests\test_arg_validation.py:243: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ..\..\..\AppData\Local\Programs\Python\Python310\lib\unittest\mock.py:1438: in __enter__ original, local = self.get_original() ..\..\..\AppData\Local\Programs\Python\Python310\lib\unittest\mock.py:1401: in get_original original = target.__dict__[name] venv02\lib\site-packages\werkzeug\local.py:311: in __get__ obj = instance._get_current_object() _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ def _get_current_object() -> T: try: obj = local.get() except LookupError: > raise RuntimeError(unbound_message) from None E RuntimeError: Working outside of request context. E E This typically means that you attempted to use functionality that needed E an active HTTP request. Consult the documentation on testing for E information about how to avoid this problem. venv02\lib\site-packages\werkzeug\local.py:508: RuntimeError
请问如何正确mock request.get_json()以解决该问题?
解决方案
错误核心原因:Flask的request是上下文局部对象(Werkzeug的LocalProxy),直接patchflask.request.get_json时,mock会尝试访问真实的request实例,但此时没有激活任何请求上下文,因此抛出Working outside of request context错误。
有两种可靠的解决方式:
方式一:Patch当前模块中导入的request对象
get_post_args函数使用的是从flask导入到当前模块的request对象,因此应该patch该模块内的request.get_json,而非顶层的flask.request.get_json。
假设get_post_args所在模块名为utils.py,修正后的测试代码如下:
import pytest from unittest.mock import patch from utils import get_post_args from errors import NoPostArguments def test_get_post_args_returns_none(): # 注意patch路径是目标函数所在模块的request对象 with patch("utils.request.get_json", return_value=None): with pytest.raises(NoPostArguments): get_post_args()
方式二:激活Flask请求上下文
通过Flask提供的test_request_context()上下文管理器创建虚拟请求上下文,让request对象能正常被访问,再执行patch操作:
import pytest from unittest.mock import patch from flask import Flask from utils import get_post_args from errors import NoPostArguments # 导入你的Flask应用实例,或临时创建一个 app = Flask(__name__) def test_get_post_args_returns_none(): with app.test_request_context(): with patch("flask.request.get_json", return_value=None): with pytest.raises(NoPostArguments): get_post_args()
额外优化:改用pytest风格断言
原测试中unittest.TestCase().assertRaises的写法不符合pytest规范,建议改用pytest内置的pytest.raises上下文管理器,代码更简洁且适配pytest生态。
内容的提问来源于stack exchange,提问作者Viktor
相关产品推荐
相关产品推荐

