Assert that a method was called in a Python unit test
假设我在Python单元测试中有以下代码:
1 2 | aw = aps.Request("nv1") aw2 = aps.Request("nv2", aw) |
有没有一种简单的方法可以断言在测试的第二行中调用了一个特定的方法(在我的例子中是
1 2 | #pseudocode: assertMethodIsCalled(aw.Clear, lambda: aps.Request("nv2", aw)) |
我使用mock(现在是py3.3+上的unittest.mock)来完成以下操作:
1 2 3 4 5 6 7 8 | 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) |
对于您的案例,可能如下所示:
1 2 3 4 5 6 7 8 9 10 | 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支持许多有用的特性,包括修补对象或模块的方法,以及检查是否调用了正确的东西等等。
自告奋勇!(买主当心!)
如果您错误地输入
是的,如果您使用的是python 3.3+。您可以使用内置的
下面是一个简单的例子:
1 2 3 4 5 | from unittest.mock import MagicMock aw = aps.Request("nv1") aw.Clear = MagicMock() aw2 = aps.Request("nv2", aw) assert aw.Clear.called |
我不知道有什么内置的。实现起来非常简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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有点生锈,我太忙了,无法详细解释。
基本上,您需要在调用原始对象的方法中放置一个代理,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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答案可以帮助您理解上面的内容。
更详细地说:
尽管答案被接受了,但由于与格伦的讨论很有趣,并且有几分钟的空闲时间,我想进一步讨论我的答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # 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) |
您可以手动或使用Pymox之类的测试框架模拟
1 2 3 4 5 6 7 8 | 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,你可以这样做:
1 2 3 4 5 6 | class MyTest(mox.MoxTestBase): def testClear(): aw = self.m.CreateMock(aps.Request) aw.Clear() self.mox.ReplayAll() aps.Request('nv2', aw) |