Python 2.7模拟类的void方法

时间:2018-11-18 11:25:29

标签: python python-2.7 unit-testing

我正在尝试使用unittests.mock模拟对象的void方法调用。

我的包裹如下所示

common
  baseupgradehandler.py

baseupgradehandler.py

class BaseUpgradeHandler(object):
    def __init__(self, upgrade_config, upgrade_state, system_config, pre_step, main_step, post_step):
        ...

    # Method call to be supressed
    def start(self, service_manifest, upgrade_bundle):
        # type: (service_version_pb2.ServiceManifest, str) -> ()
        ...

在测试代码中,我尝试像documentation中所述,模拟对start()的调用,如下所示。

from workflow.upgradeworkflow import UpgradeWorkflow
from common.serviceregistry import ServiceRegistry
# The above imports are at the start of the test file
...
with patch('common.baseupgradehandler.BaseUpgradeHandler') as handler_mock:  # type: Mock
    handler_mock.return_value.start.return_value = ''                    
    wf = UpgradeWorkflow(ServiceRegistry(self.service_bundle, config, sys_config, state),
                         config,
                         state,
                         sys_config)

BaseUpgradeHandler对象由get_upgrade_handler()的{​​{1}}方法返回。在测试中执行上述代码时,我看到ServiceRegistry仍在被调用。

有人可以让我知道如何模拟对BaseUpgradeHandler.start()的调用,以便不调用该方法吗?

编辑

如果我按如下所示更改补丁代码,则它可以按预期工作,start()被嘲笑,BaseUpgradeHandler没有被调用。

start

有人可以向我解释为什么我也必须修补with patch('common.baseupgradehandler.BaseUpgradeHandler') as handler_mock: # type: Mock handler_mock.return_value.start.return_value = '' with patch('common.serviceregistry.ServiceRegistry') as serviceregistry_mock: # type: Mock serviceregistry_mock.return_value.get_upgrade_handler.return_value = handler_mock wf = UpgradeWorkflow(ServiceRegistry(self.service_bundle, config, sys_config, state), config, state, sys_config) wf.start() 吗?

2 个答案:

答案 0 :(得分:1)

您提供的代码不足以查看导致问题的部分。我们需要确保查看模块serviceregistry,但我会做一个有根据的猜测:

您有一个文件a.py(又名baseupgradehandler),如下所示:

class A:
    def method(self):
        print("It's real!")

还有一个文件b.py(又名serviceregistry),如下所示:

from a import A

class B:
    def get_A(self):
        return A()

在测试文件中,您可以执行以下操作:

import unittest
from unittest.mock import patch
from b import B
from a import A

游戏结束!

B模块现在已经引用了原始的A类。 之后,您patch('a.A')更改了a模块中的引用,但是patch无法知道B有对原始A的引用。

您可以通过三种方式解决此问题:

  • 修补方法:这将修改现有的类,以便对该类的所有引用都将被自动修补
  • 也修补b.A

    with patch('a.A') as h_a, patch('b.A') as h_b:
        h_a.return_value.method.return_value = ''
        h_b.return_value.method.return_value = ''
    
  • 在修补之前,避免导入模块(可能不可行或一个好主意):

    import unittest
    from unittest.mock import patch
    
    class MyTest(unittest.TestCase):
        def test_one(self):
            with patch('a.A') as h:
                h.return_value.method.return_value = ''
                from b import B
                B().get_A().method()
    

答案 1 :(得分:0)

一段时间以来,我一直在使用unittest.mocks,有时我又在重新发明轮子。我决定将Mockito纳入我的项目,现在情况看起来好多了。任何一种模拟验证都非常简单,如果可以的话,我绝对鼓励您将模拟成为您的库的一部分。该库有很好的文档,到目前为止,它比unittest.mock恕我直言要容易得多。