Python模拟错误:停止调用未启动的修补程序

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有什么解决办法,这在某种程度上禁止_exitmock或任何其他方法.我猜测内容在某种程度上不足以创建整个场景,但这是我能做的最好的.

此外,如果我不提供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_effectreturn_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.)

Mic*_*ico 9

关于的第一个基础patch:

在函数体或with语句的主体内部,使用新对象修补目标.当函数/ with语句退出补丁时,撤消.

如果new省略,则将目标替换为a MagicMock.如果 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)
/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
Run Code Online (Sandbox Code Playgroud)