在单元测试中模拟Flask的request.get_json时出现“RuntimeError: Working outside of request context”

0 投票
1 回答
28 浏览
提问于 2025-04-14 17:42

我正在为一个大型的Flask应用程序的后端开发单元测试。我在测试一个辅助函数get_post_args(),看看它是否能正确处理空请求:

from flask import request
from unittest.mock import patch

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

有没有人知道怎么正确模拟request.get_json()?

1 个回答

0

Flask 提供了一个叫做 app.test_request_context() 的方法,这个方法可以让你创建任何你需要的假请求环境。在这种情况下,使用模拟工具就显得有些多余了。

最简单的用法是:

# Assuming you're inside a class derived from unittest.TestCase
def test_01_simplest(self):
    with your_app.test_request_context(method='POST', json={'message': "Hello!"}):
        result = get_post_args()
        self.assertIn('message', result)

下面是一个例子,用来测试捕捉 NoPostArguments,因为 get_json 不一定总是返回一个 dict

def test_02_json_not_always_a_dict(self):
    with your_app.test_request_context(method='POST', json=[]), \
           self.assertRaises(NoPostArguments):
        get_post_args()

你可能在表达空的 JSON 请求时遇到过困难。这个问题可以通过传递一些额外的参数来解决,这些参数是为 werkzeug.test.EnvironBuilder 定义的。顺便提一下,在这种情况下,request.get_json() 会首先失败,并抛出 werkzeug.exceptions.BadRequest。所以,正确的测试应该是这样的:

import werkzeug.exceptions

...

def test_03_empty_json_payload(self):
    with your_app.test_request_context(method='POST',
                                       json=None,  # may be omitted
                                       content_type="application/json"), \
           self.assertRaises(werkzeug.exceptions.BadRequest):
        get_post_args()

撰写回答