Python 条件模拟:如果条件不匹配,则调用原始函数
如何在模拟中有条件地调用原始方法 在这个例子中,我只想在Python 条件模拟:如果条件不匹配,则调用原始函数,python,mocking,Python,Mocking,如何在模拟中有条件地调用原始方法 在这个例子中,我只想在bar=='x'时伪造一个返回值。否则我想调用原始方法 def mocked_some_method(bar): if bar=='x': return 'fake' return some_how_call_original_method(bar) with mock.patch('mylib.foo.some_method', mocked_some_method): do_some_stuff
bar=='x'
时伪造一个返回值。否则我想调用原始方法
def mocked_some_method(bar):
if bar=='x':
return 'fake'
return some_how_call_original_method(bar)
with mock.patch('mylib.foo.some_method', mocked_some_method):
do_some_stuff()
我知道这有点奇怪。如果我想在do\u stuff()中伪造mylib.foo.some\u方法,它应该是无条件的。对some\u方法
的所有(而不是一些)调用都应该模拟
在我的例子中,它是一个集成测试,而不是一个小型单元测试和mylib.foo。一些方法是一种经常使用的分派器。有一次我需要伪造结果
更新
我四年前写过这个问题。今天,做有条件的嘲弄感觉很奇怪。模拟应该只在测试中使用。测试(和生产代码)应该简单且小。测试应该是无条件的。在我写这个问题时,我们仍然使用大量的生产方法和长时间的测试。今天,我遵循这些规则(简单方法、无条件测试…)。我把我的发现写了下来:如果你只需要替换行为而不关心mock的assert函数调用,你可以使用新的参数;否则,您可以使用该选项进行调用
我猜some_method
是一个对象方法(而不是staticmethod
),因此您需要一个引用来调用它的对象。包装器应该声明对象和修补程序使用autospec=True
作为第一个参数,以便为副作用
案例使用正确的签名
最后一个技巧是保存原始方法引用并使用它进行调用
orig = mylib.foo.some_method
def mocked_some_method(self, bar):
if bar=='x':
return 'fake'
return orig(self, bar)
#Just replace:
with mock.patch('mylib.foo.some_method', new=mocked_some_method):
do_some_stuff()
#Replace by mock
with mock.patch('mylib.foo.some_method', side_effect=mocked_some_method, autospec=True) as mock_some_method:
do_some_stuff()
assert mock_some_method.called
下面是一个示例,说明如何在需要时动态修补类方法并执行原始方法
需要测试的代码
mock.patcher的ContextManager测试助手
示例测试方法,当您想要返回原始值时,请参见results var的值,\uuuuuuuuuuuuuuuuuuuuuuuuuuuu
从某处导入checkmock方法
从测试助手导入模拟补丁方法
def测试_包装(自):
path='.CheckMockMethod.get_value_x_10'
orig=CheckMockMethod.get_value_x_10
结果=[1000',原始值,3000]
使用模拟修补方法原始(路径,原始方法=原始,结果=结果):
包装器_func()
结果
用mock你会看到
1000,20,3000
其中1000和3000是修补值,20是原始值这里的“静态”是什么意思:“我猜某些方法不是静态的…”你是对的。mock-as-wrapper不是最佳实践。我更新了问题以反映这一点。@guettli从您的问题中,我可以猜测某些方法
不是@staticmethod
,而是对象方法。。。。我将通过删除旁注来更改我的答案,但它应该已经符合您的要求。链接[我的编程指南]已断开。@oszkar我更新了链接。谢谢你的提示。
class CheckMockMethod:
def get_value_x_10(self, value):
"""Method which is going to be patched"""
return value*10
def wrapper_func():
"""Function which is called from test"""
for i in [1, 2, 3]:
print(CheckMockMethod().get_value_x_10(i))
import mock
from contextlib import contextmanager
@contextmanager
def mock_patch_method_original(mock_path, original_method, results):
results = iter(results)
def side_effect(self, *args, **kwargs):
value = next(results)
if value == '__original__':
side_effect.self = self
return original_method(self, *args, **kwargs)
else:
return value
patcher = mock.patch(mock_path, autospec=True, side_effect=side_effect)
yield patcher.start()
patcher.stop()
from somewhere import CheckMockMethod
from test_helpers import mock_patch_method_original
def test_wrapper(self):
path = '<import-path>.CheckMockMethod.get_value_x_10'
orig = CheckMockMethod.get_value_x_10
results = [1000, '__original__', 3000]
with mock_patch_method_original(path, original_method=orig, results=results):
wrapper_func()