【问题标题】:Flask test client and unittest: mocking global object?烧瓶测试客户端和单元测试:模拟全局对象?
【发布时间】:2021-09-30 12:08:03
【问题描述】:

我有一个使用全局对象 data_loader 的烧瓶应用程序。

主要的烧瓶文件(我们称之为main.py)开始如下:

app = Flask('my app')
...
data_loader = DataLoader(...)

稍后,这个全局 data_loader 对象在 webserver 的路由方法中被调用:

class MyClass(Resource):
    def get(self):
      data_loader.load_some_data()
      # ... process data, etc

使用unittest,我希望能够修补load_some_data() 方法。我正在使用烧瓶test_client

from my_module.main import app

class MyTest(unittest.TestCase):

    @classmethod
    def setUpClass(cls) -> None:
        cls.client = app.test_client('my test client')

如何在MyTest 的后续测试中修补data_loader 方法?我已经尝试过这种方法,但它不起作用(尽管data_loader 似乎在某些时候被替换了):

 @unittest.mock.patch('my_module.main.DataLoader')
 def my_test(self, DataLoaderMock):
     
     data_loader = DataLoaderMock.return_value
     data_loader.my_method.return_value = 'new results (patched)'

     with app.test_client() as client:
         response = client.get(f'/some/http/get/request/to/MyTest/route', 
                               query_string={...})

     # ... some assertions to be tested ...

似乎data_loader 在 Flask 应用程序中从未真正被替换。

另外,在 Flask 服务器中有一个全局变量是否被认为是“好习惯”,还是应用程序应该将它存储在里面?

谢谢

【问题讨论】:

  • 谢谢。祝你好运 ???? ??????

标签: flask mocking python-unittest


【解决方案1】:

关于mockingpatch.object可用于修改对象属性:

@unittest.mock.patch.object(data_loader, 'my_method')
def my_test(self, my_method_mock):
    my_method_mock.return_value = 'new results (patched)'
    with app.test_client() as client:
        response = client.get(f'/some/http/get/request/to/MyTest/route', 
                              query_string={...})

        my_method_mock.assert_called() # ok!

我的有趣见解的解决方案是:

import unittest
from unittest.mock import patch


class MyTest(unittest.TestCase):
    def test_get(self):
        client = app.test_client('my test client')
        patcher = patch('{package_here}.{module_here}.DataLoader.load_some_data', return_value={'status': 1})
        patcher.start()
        self.assertDictEqual(client.get('/').json, {'status': 1})
        patcher.stop()
        # or
        with patch('{package_here}.{module_here}.DataLoader.load_some_data', return_value={'status': 1}):
            self.assertDictEqual(client.get('/').json, {'status': 1})

关于“良好实践”和全局变量。是的,我在各种项目中见过全局变量。但我不建议使用全局变量。因为:

  • 它可能导致递归导入和依赖地狱。我曾使用带有递归导入的大型 Flask 应用程序。这真的很痛苦。而且你不可能在短时间内解决所有问题。
  • 假设您有一个测试,mockingglobal variables。我认为当你有一个相当大的服务时,重构会更加困难。
  • 单独的导入和初始化确实更简单,更可配置。在这种情况下,所有工作都在一个方向import all dependencies -> load config -> initialization -> run。在其他情况下,您将拥有import -> new instance -> new instance -> import -> ...
  • 内存泄漏的另一个原因。

也许全局变量对于独立的packagesmodules 等来说不是坏方法,但对于项目来说不是。我还想推荐使用some additional tools。这不仅会使编写测试变得更容易,而且还可以省去你的麻烦。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-07
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-27
    相关资源
    最近更新 更多