Python 模拟导入模块中的函数

问题描述 投票:0回答:5

我想了解如何

@patch
导入模块中的函数。

这就是我目前所处的位置。

应用程序/mocking.py:

from app.my_module import get_user_name

def test_method():
  return get_user_name()

if __name__ == "__main__":
  print "Starting Program..."
  test_method()

应用程序/my_module/__init__.py:

def get_user_name():
  return "Unmocked User"

测试/模拟测试.py:

import unittest
from app.mocking import test_method 

def mock_get_user():
  return "Mocked This Silly"

@patch('app.my_module.get_user_name')
class MockingTestTestCase(unittest.TestCase):

  def test_mock_stubs(self, mock_method):
    mock_method.return_value = 'Mocked This Silly')
    ret = test_method()
    self.assertEqual(ret, 'Mocked This Silly')

if __name__ == '__main__':
  unittest.main()

这确实如我所期望的那样工作。 “修补”模块仅返回

get_user_name
的未模拟值。如何模拟导入到正在测试的命名空间中的其他包中的方法?

python unit-testing python-unittest python-mock
5个回答
308
投票

当您使用

patch
包中的
unittest.mock
装饰器时,您将在正在测试的命名空间(在本例中为
app.mocking.get_user_name
)中修补它,而不是在函数导入的命名空间中(在本例中)
app.my_module.get_user_name
)。

要执行您用

@patch
描述的操作,请尝试以下操作:

from mock import patch
from app.mocking import test_method 

class MockingTestTestCase(unittest.TestCase):
    
    @patch('app.mocking.get_user_name')
    def test_mock_stubs(self, test_patch):
        test_patch.return_value = 'Mocked This Silly'
        ret = test_method()
        self.assertEqual(ret, 'Mocked This Silly')

标准库文档包含一个有用的部分描述了这一点。


26
投票

虽然 Matti John 的回答解决了您的问题(也帮助了我,谢谢!),但是,我建议本地化用模拟函数替换原始的“get_user_name”函数。这将允许您控制何时替换该函数以及何时不替换该函数。此外,这还允许您在同一测试中进行多次替换。为此,请以非常相似的方式使用“with”语句:

from mock import patch

class MockingTestTestCase(unittest.TestCase):

    def test_mock_stubs(self):
        with patch('app.mocking.get_user_name', return_value = 'Mocked This Silly'):
            ret = test_method()
            self.assertEqual(ret, 'Mocked This Silly')

4
投票

接受的答案是正确的,使用

patch
您必须考虑导入它的名称空间。但是想象一下,您想要在全局范围内覆盖 actual 实现,无论它在哪里导入,您都可以
monkeypatch
实现:

@pytest.fixture(autouse=True)
def get_user_name_mock(monkeypatch):
    _mock = MagicMock()
    monkeypatch.setattr(app.my_module, app.my_module.get_user_name.__name__, _mock )
    return _mock 

在您的测试中,只需添加夹具名称作为参数,就像默认夹具行为一样。

在我的项目中,我使用它来覆盖我的全局配置值解析,以捕获可能导致未定义行为的 unmocked

config.get
调用。

编辑:链接到文档:https://pytest.org/en/7.3.x/how-to/monkeypatch.html#how-to-monkeypatch-mock-modules-and-environments


0
投票

基于已经给出的答案,尤其是 Matti 的答案,它帮助解决了我的修补问题(我正在修补替换原始函数,而不是模块中的函数调用),我更喜欢在没有额外的情况下完成此操作

mock
图书馆。我不确定 10 年前这是否是作为标准 Python 安装的一部分提供的,但今天,它是一个单独的安装,我宁愿让我的 pyproject.toml 尽可能干净和轻量。这是我用 unnitest 解决它的方法。


from mocking import test_method
import unittest
from unittest.mock import patch


class MockingTestTestCase(unittest.TestCase):
    @patch("mocking.get_user_name")
    def test_mock_stubs(self, test_patch):
        test_patch.return_value = "Mocked This Silly"
        ret = test_method()
        self.assertEqual(ret, "Mocked This Silly")

        self.assertEqual(test_patch.call_count, 1)

我还添加了另一个断言语句。确保调用特定函数是个好主意。对于具有多个编码器的大型代码库,这可能至关重要。


-3
投票

除了 Matti John 的解决方案之外,您还可以在

app/mocking.py
中导入模块而不是函数。

# from app.my_module import get_user_name
from app import my_module

def test_method():
  return my_module.get_user_name()

if __name__ == "__main__":
  print "Starting Program..."
  test_method()
© www.soinside.com 2019 - 2024. All rights reserved.