vks*_*vks 2 python unit-testing mocking
我有一个函数让我们说def temp.我正在以下面的方式嘲笑它:
msg = "Mocked!!!!!!!"
@mock.patch.object(Someothermodule.Someclass,'Somefunction',create=True,side_effect=Error(Error.something,msg))
def temp(self,mock_A):
Run Code Online (Sandbox Code Playgroud)
它是一种巨大的项目,所以斜面发布细节here.But发生了什么是功能temp确实得到嘲笑,我也得到正确的消息,但我后来得到stop called on unstarted patcher和程序在这里fails.Is有什么解决办法,这在某种程度上禁止_exit的mock或任何其他方法.我猜测内容在某种程度上不足以创建整个场景,但这是我能做的最好的.
此外,如果我不提供mock_A功能会发生什么def temp.在这种情况下补丁工作的是什么?
编辑:
我有一个解决方法,其中我已定义我的补丁如下:
@mock.patch.object(Someothermodule.Someclass,'Somefunction',create=True,some_function)
def temp(self):
Run Code Online (Sandbox Code Playgroud)
现在的问题是
1)当我使用side_effect或return_value我必须提供一个mock_object装饰后面的功能.
2)当我刚使用函数代替时side_effect,我不需要在装饰器后面的函数中提供mock_object.
所以,
When we dont pass mock_obj,do all the function know about the patch?how exactly is this working?What is the difference between the scenario wherein we have to explicitly pass mock_object and wherein we dont have to?
(PS是这与new_callable定义中的事物有关patch.)
关于的第一个基础patch:
在函数体或with语句的主体内部,使用新对象修补目标.当函数/ with语句退出补丁时,撤消.
如果
new省略,则将目标替换为aMagicMock.如果patch()用作装饰器并且省略new,则创建的mock将作为额外参数传递给装饰函数.如果patch()用作上下文管理器,则上下文管理器返回创建的模拟.
第一个告诉我们补丁在函数体(装饰器案例)中充当上下文管理器.第二个告诉你是否没有指定new参数(第一个在patch类函数中定位参数之后)补丁创建一个新MagicMock()对象来修补目标,并且创建的模拟作为额外参数传递给修饰函数.
这解释了问题的最后一部分,因为
@mock.patch.object(Someothermodule.Someclass,'Somefunction',create=True,some_function)
def temp(self):
Run Code Online (Sandbox Code Playgroud)
你用object替换Someothermodule.Someclass.Somefunction,而decorator不需要传递给它.some_function patchtemp
现在回到问题的根源:stop called on unstarted patcher错误消息.
这意味着你尝试删除一次补丁.怎么可能呢?
如果您查看mock.py代码,您可以很容易地理解该补丁不支持补丁堆栈,并且它被设计为每个上下文只执行一次.换句话说,如果您尝试修补已在上下文中修补的内容,则会出现相同的错误.在答案的最后,我提供了一个生成错误的合成示例.
没有看到你的代码,我可以这样做,为什么你的问题的一些猜测,猜测的接缝,如果你不使用模拟,而是由一个功能修补方法固定.恕我直言,它只是偶然的工作,你玩某种竞争条件.
我能做的就是给你一个方法来解决你的问题和一个好的工作.
要想知道你是否__exit__在补丁中调用了一次,只需将其patch用作上下文管理器并覆盖__exit__.未经测试的方法可能是:
def my_wrapper(f):
count = 0
@functools.wraps(f)
def exit(*args, **kwargs):
count += 1
print ("EXIT CALL {}".format(count))
f(*args, **kwargs)
def temp(self):
patcher = mock.patch.object(Someothermodule.Someclass,'Somefunction',create=True,side_effect=Error(Error.something,msg))
patcher.__exit__ = my_wrapper(patcher.__exit__)
with patcher as mock_A:
#... your stuff
Run Code Online (Sandbox Code Playgroud)
最后的解决方法(如果你没有找到任何修复双重调用的方法就使用)
class MyStackPatch():
def __init__(p):
self._patcher = p
self._count = 0
self._lock = threading.RLock()
def __enter__(self):
with self._lock:
if not self._count:
self._patcher.start()
self._count += 1
def __exit__(self, *exc_info):
with self._lock:
self._count -= 1
if not self._count:
self._patcher.stop()
def temp(self):
patcher = mock.patch.object(Someothermodule.Someclass,'Somefunction',create=True,side_effect=Error(Error.something,msg))
with MyStackPatch(patcher) as mock_A:
#... your stuff
Run Code Online (Sandbox Code Playgroud)
它很容易扩展它并编写装饰器......但我认为它足以解决这个问题.
这是一个生成stop called on unstarted patcher错误消息的合成示例.为了生成它,我们必须再次调用补丁,而我们只是在补丁上下文中.我们可以通过递归或线程来实现,递归在这种情况下是非常奇怪的.
target调用patched方法并启动一个调用的新线程t().我使用了一个Event()强制错误并使竞争状态始终为真.
import threading
from mock import patch
def somemethod():
pass
e = threading.Event()
@patch("__main__.somemethod")
def t(state,m):
if state:
e.set()
while e.is_set():
e.wait(.5)
somemethod()
def target():
threading.Thread(target=t,args=(True,)).start()
while not e.is_set():
e.wait(.1)
t(False)
e.clear()
target()
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)/usr/bin/python2.7 /home/damico/PycharmProjects/untitled1/mock_and_thread.py Exception in thread Thread-1: Traceback (most recent call last): File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner self.run() File "/usr/lib/python2.7/threading.py", line 763, in run self.__target(*self.__args, **self.__kwargs) File "/usr/local/lib/python2.7/dist-packages/mock.py", line 1214, in patched patching.__exit__(*exc_info) File "/usr/local/lib/python2.7/dist-packages/mock.py", line 1376, in __exit__ raise RuntimeError('stop called on unstarted patcher') RuntimeError: stop called on unstarted patcher