假设我在Python单元测试中具有以下代码:
aw = aps.Request("nv1")
aw2 = aps.Request("nv2", aw)
有没有一种简单的方法可以断言aw.Clear()
在测试的第二行期间调用了特定方法(在我的情况下)?例如是否有这样的事情:
#pseudocode:
assertMethodIsCalled(aw.Clear, lambda: aps.Request("nv2", aw))
Answers:
我为此使用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)
对于您的情况,它可能看起来像这样:
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支持许多有用的功能,包括修补对象或模块的方式以及检查是否调用了正确的东西等。
买者自负!(请当心!)
如果您输入错误的assert_called_with
(到assert_called_once
或assert_called_wiht
)您的测试可能仍在运行,因为Mock会认为这是一个模拟的函数并且很乐意进行,除非使用autospec=true
。有关更多信息,请阅读assert_call_once:Threat或Menace。
是的,如果您使用的是Python 3.3+。您可以使用内置的unittest.mock
断言方法调用。对于Python 2.6+,请使用rolling backport Mock
,这是相同的。
这是您情况下的简单示例:
from unittest.mock import MagicMock
aw = aps.Request("nv1")
aw.Clear = MagicMock()
aw2 = aps.Request("nv2", aw)
assert aw.Clear.called
我不知道任何内置的东西。实现起来非常简单:
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()
这就要求对象本身不会修改self.b,这几乎总是正确的。
是的,我可以给您提纲,但是我的Python有点生疏,我太忙了无法详细解释。
基本上,您需要在将调用原始方法的方法中放置一个代理,例如:
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)
这个关于callable的StackOverflow答案可以帮助您理解以上内容。
更详细地:
尽管答案已被接受,但由于与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)
您可以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
使用pymox,您可以这样做:
class MyTest(mox.MoxTestBase):
def testClear():
aw = self.m.CreateMock(aps.Request)
aw.Clear()
self.mox.ReplayAll()
aps.Request('nv2', aw)