Python2.7模拟类的void方法
我试图使用Python2.7模拟类的void方法,python,python-2.7,unit-testing,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): ... #
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) -> ()
...
在我的测试代码中,我试图模拟对start()
的调用,如下所述
BaseUpgradeHandler
对象由get\u upgrade\u handler()的ServiceRegistry
方法返回。当我在测试中执行上述代码时,我看到仍在调用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()
有人能告诉我如何模拟对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()
有人能解释一下为什么我还需要修补ServiceRegistry吗?您提供的代码不足以看到导致问题的部分。我们需要查看模块serviceregistry
,以确定这一点,但我会进行一个有根据的猜测:
您有一个文件a.py
(也称为baseupgradehandler
),如下所示:
class A:
def method(self):
print("It's real!")
from a import A
class B:
def get_A(self):
return A()
还有一个文件b.py
(又称serviceregistry
),如下所示:
class A:
def method(self):
print("It's real!")
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
模块中的引用,但是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 = ''
- 避免在打补丁之前导入模块(可能不可行或是个好主意):
我使用unittest.mock已经有一段时间了,有时我还会重新发明轮子。我决定让mockito成为我项目的一部分,现在事情看起来好多了。任何类型的模拟验证都非常简单,如果可以的话,我坚决鼓励您将mockito作为库的一部分。这个库有一个很好的文档,到目前为止,它比unittest.mock IMHO更容易。patch
用作上下文管理器,只在块中使用对方法进行修补。你可能在外面叫它。如果您想为整个测试方法修补该方法,请使用patch
作为装饰器。另外:如果您想将('mcommon.baseupgradehandler.baseupgradehandler.start')修补为方法:method.return\u value='
@Bakuriu-不,我不是在外部调用它,那么您可以只模拟该方法。我已经编辑了这个问题。我不想修补整个测试方法。因此,我将与一起使用。如果我按照你的建议,将补丁('mcommon.baseupgradehandler.baseupgradehandler.start')作为方法:method.return_value='
,那么这是可以预期的。我在BaseUpgradeHandler
中有多种方法可以模拟。那么,我必须像你建议的那样模仿每一种方法吗?或者还有其他方法吗?我无法重现你的问题。您如何检查是否调用了真正的方法?我们需要一个完整的例子来重现这个问题,以理解为什么补丁在您的案例中不起作用。可能您是通过修补之前获取的引用访问该类。如果你把patch
放在装饰者的位置,这种行为还会发生吗?@Bakuriu-我已经编辑了我的问题。它是更大代码库的一部分。我将尝试有一个完整的玩具的例子。但是,如果上面的编辑提供了任何线索,一定要告诉我。只是一个提示:你来自一个庞大的代码库并不重要。首先“注释掉”该测试未使用的所有代码。然后注释掉该测试未直接使用的所有类/函数/方法。然后将查找文件/数据库的函数的实现更改为只返回固定结果。现在,不管您来自哪里,您应该拥有很少的类,其中几乎没有实现。看看问题是否仍然存在。如果没有,请尝试添加更多代码并重复。大多数时候,通过这个过程,你会找到答案。