【问题标题】:How to mock flask.request with unittest?如何用 unittest 模拟 flask.request?
【发布时间】:2019-06-18 10:04:40
【问题描述】:

在我的应用程序中,我有一个从烧瓶服务调用的类。这个类从 flask.request 对象中获取一些属性,所以我想模拟它们。

我的一个实现示例是:

myClassHelper.py

from flask import request

class MyClassHelper:

    def __init__(self, addRequestData=False):
        self.attribute = 'something'
        self.path = request.path if addRequestData else None

    def __str__(self):
        return 'attribute={0}; path={1};'.format(self.attribute, self.path)

myClassHelperTest.py

from unittest import TestCase
from unittest.mock import MagicMock

import flask

from myClassHelper import MyClassHelper


class MyClassHelperTest(TestCase):
    def setUp(self):
        self.path = '/path'

        self.unmock = {}
        self.unmock['flask.request'] = flask.request

        flask.request = MagicMock(path='/path')


    def tearDown(self):
        flask.request = self.unmock['flask.request']

    def test_printAttributes(self):
        expectedResult = 'attribute=something; path={0};'.format(self.path)
        result = str(MyClassHelper(addRequestData=True))
        self.assertEqual(expectedResult, result)

当我导入from myClassHelper import MyClassHelper 时,问题就来了。这将转到 MyClassHelper 中的导入 from flask import request。所以测试类的setUp方法中的mock,并没有被应用。

这可以通过导入flask 并访问flask.request.path 之类的路径属性来解决。但我想避免导入完整的烧瓶模块。

有没有办法为使用来自 flask.request 的属性的方法创建单元测试,模拟它们而不使用烧瓶测试客户端?

【问题讨论】:

    标签: python flask mocking python-import python-unittest


    【解决方案1】:

    一定有办法,但是像这样的单元测试代码无论如何都会给你带来麻烦。 SUT 正在访问由另一个模块管理的全局状态,因此您的测试需要正确设置该全局状态。这可以通过按原样使用另一个模块来完成,因为有充分的理由(加上它不再是单元测试),或者通过猴子修补它。这通常很棘手(正如您已经发现的那样)并且很脆弱(如果您更改在生产代码中导入内容的方式,您的测试将会中断;如果相关行为没有改变,为什么会发生这种情况?)

    解决此类问题的方法是让您的对象请求他们需要的东西,而不是在全局状态下寻找它们。因此,如果MyClassHelper 的所有实例都需要一个路径,只需让它请求一个路径。让调用代码找出路径的来源。具体来说,您的测试可以轻松提供固定路径。

    如果你遵循这个原则,你的测试会是这样的:

    class MyClassHelperTest(TestCase):
        def test_printAttributes(self):
            expectedResult = 'attribute=something; path=/path;'
            result = str(MyClassHelper('/path'))
            self.assertEqual(expectedResult, result)
    

    比以前简单多了。这就是你让它通过的方式:

    class MyClassHelper:
        def __init__(self, path):
            self.attribute = 'something'
            self.path = path
    
        def __str__(self):
            return 'attribute={0}; path={1};'.format(self.attribute, self.path)
    

    如果测试中的行为是你想要的,你真的不需要attribute。我把它留在那里是为了少偏离你的原始代码。我假设您有其他测试来说明为什么实际需要它。

    【讨论】:

    • 感谢您的回复,我会将这些更改应用到我的课程中以简化它们并避免未来的测试问题
    猜你喜欢
    • 1970-01-01
    • 2022-06-14
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-20
    相关资源
    最近更新 更多