断言在 Python 单元测试中调用了一个方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3829742/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Assert that a method was called in a Python unit test
提问by Mark Heath
Suppose I have the following code in a Python unit test:
假设我在 Python 单元测试中有以下代码:
aw = aps.Request("nv1")
aw2 = aps.Request("nv2", aw)
Is there an easy way to assert that a particular method (in my case aw.Clear()) was called during the second line of the test? e.g. is there something like this:
是否有一种简单的方法可以断言aw.Clear()在测试的第二行期间调用了特定方法(在我的情况下)?例如有没有这样的东西:
#pseudocode:
assertMethodIsCalled(aw.Clear, lambda: aps.Request("nv2", aw))
采纳答案by Macke
I use Mock(which is now unittest.mockon py3.3+) for this:
为此,我使用Mock(现在是 py3.3+ 上的unittest.mock):
from mock import patch
from PyQt4 import Qt
@patch.object(Qt.QMessageBox, 'aboutQt')
def testShowAboutQt(self, mock):
self.win.actionAboutQt.trigger()
self.assertTrue(mock.called)
For your case, it could look like this:
对于您的情况,它可能如下所示:
import mock
from mock import patch
def testClearWasCalled(self):
aw = aps.Request("nv1")
with patch.object(aw, 'Clear') as mock:
aw2 = aps.Request("nv2", aw)
mock.assert_called_with(42) # or mock.assert_called_once_with(42)
Mock supports quite a few useful features, including ways to patch an object or module, as well as checking that the right thing was called, etc etc.
Mock 支持很多有用的功能,包括修补对象或模块的方法,以及检查是否调用了正确的东西等。
Caveat emptor!(Buyer beware!)
买者自负!(买家当心!)
If you mistype assert_called_with(to assert_called_onceor assert_called_wiht) your test may still run, as Mock will think this is a mocked function and happily go along, unless you use autospec=true. For more info read assert_called_once: Threat or Menace.
如果您输入错误assert_called_with(toassert_called_once或assert_called_wiht),您的测试可能仍会运行,因为 Mock 会认为这是一个模拟函数并愉快地继续,除非您使用autospec=true. 有关更多信息,请阅读assert_Called_once: Threat or Menace。
回答by Max Shawabkeh
You can mock out aw.Clear, either manually or using a testing framework like pymox. Manually, you'd do it using something like this:
您可以aw.Clear手动模拟或使用pymox 之类的测试框架进行模拟。手动,你会使用这样的东西:
class MyTest(TestCase):
def testClear():
old_clear = aw.Clear
clear_calls = 0
aw.Clear = lambda: clear_calls += 1
aps.Request('nv2', aw)
assert clear_calls == 1
aw.Clear = old_clear
Using pymox, you'd do it like this:
使用 pymox,你会这样做:
class MyTest(mox.MoxTestBase):
def testClear():
aw = self.m.CreateMock(aps.Request)
aw.Clear()
self.mox.ReplayAll()
aps.Request('nv2', aw)
回答by Andy Dent
Yes, I can give you the outline but my Python is a bit rusty and I'm too busy to explain in detail.
是的,我可以给你大纲,但我的 Python 有点生疏,我太忙了,无法详细解释。
Basically, you need to put a proxy in the method that will call the original, eg:
基本上,您需要在调用原始方法的方法中放置一个代理,例如:
class fred(object):
def blog(self):
print "We Blog"
class methCallLogger(object):
def __init__(self, meth):
self.meth = meth
def __call__(self, code=None):
self.meth()
# would also log the fact that it invoked the method
#example
f = fred()
f.blog = methCallLogger(f.blog)
This StackOverflow answerabout callable may help you understand the above.
这个关于 callable 的StackOverflow 回答可能会帮助你理解上述内容。
In more detail:
更详细地:
Although the answer was accepted, due to the interesting discussion with Glenn and having a few minutes free, I wanted to enlarge on my answer:
虽然答案被接受了,但由于与 Glenn 进行了有趣的讨论并且有几分钟的空闲时间,我想扩大我的答案:
# helper class defined elsewhere
class methCallLogger(object):
def __init__(self, meth):
self.meth = meth
self.was_called = False
def __call__(self, code=None):
self.meth()
self.was_called = True
#example
class fred(object):
def blog(self):
print "We Blog"
f = fred()
g = fred()
f.blog = methCallLogger(f.blog)
g.blog = methCallLogger(g.blog)
f.blog()
assert(f.blog.was_called)
assert(not g.blog.was_called)
回答by Glenn Maynard
I'm not aware of anything built-in. It's pretty simple to implement:
我不知道有什么内置的。实现起来非常简单:
class assertMethodIsCalled(object):
def __init__(self, obj, method):
self.obj = obj
self.method = method
def called(self, *args, **kwargs):
self.method_called = True
self.orig_method(*args, **kwargs)
def __enter__(self):
self.orig_method = getattr(self.obj, self.method)
setattr(self.obj, self.method, self.called)
self.method_called = False
def __exit__(self, exc_type, exc_value, traceback):
assert getattr(self.obj, self.method) == self.called,
"method %s was modified during assertMethodIsCalled" % self.method
setattr(self.obj, self.method, self.orig_method)
# If an exception was thrown within the block, we've already failed.
if traceback is None:
assert self.method_called,
"method %s of %s was not called" % (self.method, self.obj)
class test(object):
def a(self):
print "test"
def b(self):
self.a()
obj = test()
with assertMethodIsCalled(obj, "a"):
obj.b()
This requires that the object itself won't modify self.b, which is almost always true.
这要求对象本身不会修改 self.b,这几乎总是正确的。
回答by Devy
Yes if you are using Python 3.3+. You can use the built-in unittest.mockto assert method called. For Python 2.6+ use the rolling backport Mock, which is the same thing.
是的,如果您使用的是 Python 3.3+。您可以使用内置的unittest.mock来断言调用的方法。对于 Python 2.6+ 使用滚动 backport Mock,这是同一件事。
Here is a quick example in your case:
这是您的案例的一个快速示例:
from unittest.mock import MagicMock
aw = aps.Request("nv1")
aw.Clear = MagicMock()
aw2 = aps.Request("nv2", aw)
assert aw.Clear.called

