Python 如何使用unittest模拟flask.request?

Python 如何使用unittest模拟flask.request?,python,flask,mocking,python-import,python-unittest,Python,Flask,Mocking,Python Import,Python Unittest,在我的应用程序中,我有一个从flask服务调用的类。这个类从flask.request对象中获取一些属性,所以我想模拟它们 我的一个实施示例是: myClassHelper.py myClassHelperTest.py 从unittest导入TestCase 从unittest.mock导入MagicMock 进口烧瓶 从myClassHelper导入myClassHelper 类MyClassHelperTest(测试用例): def设置(自): self.path='/path' self

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

我的一个实施示例是:

myClassHelper.py myClassHelperTest.py
从unittest导入TestCase
从unittest.mock导入MagicMock
进口烧瓶
从myClassHelper导入myClassHelper
类MyClassHelperTest(测试用例):
def设置(自):
self.path='/path'
self.unmack={}
self.unmack['flask.request']=flask.request
flask.request=MagicMock(路径='/path')
def拆卸(自):
flask.request=self.unck['flask.request']
def测试_打印属性(自身):
expectedResult='attribute=something;路径={0};'。格式(self.path)
结果=str(MyClassHelper(addRequestData=True))
self.assertEqual(预期结果、结果)
当我从myClassHelper导入myClassHelper时,问题出现了。这将转到MyClassHelper内的从flask导入请求导入
。因此,在测试类的setUp方法中没有应用mock

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


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

一定有办法,但这样的单元测试代码无论如何都会给您带来麻烦。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)

如果测试中的行为是您想要的,那么您实际上不需要
属性。我把它放在那里是为了减少与原始代码的偏差。我假设您还有其他测试可以说明为什么需要它。

感谢您的回复,我将把这些更改应用到我的类中,以简化它们,避免将来的测试问题
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)