簡體   English   中英

斷言在Python單元測試中調用了一個方法

[英]Assert that a method was called in a Python unit test

假設我在Python單元測試中有以下代碼:

aw = aps.Request("nv1")
aw2 = aps.Request("nv2", aw)

是否有一種簡單的方法斷言在測試的第二行期間調用了一個特定的方法(在我的情況下是aw.Clear() )? 例如,有這樣的事情:

#pseudocode:
assertMethodIsCalled(aw.Clear, lambda: aps.Request("nv2", aw))

我使用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_onceassert_called_wiht ),你的測試可能仍會運行,因為Mock會認為這是一個模擬函數並且很樂意繼續運行,除非你使用autospec=true 有關更多信息,請閱讀assert_called_once:威脅或威脅

是的,如果您使用的是Python 3.3+。 您可以使用內置的unittest.mock來斷言調用的方法。 對於Python 2.6+,使用滾動后端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)

這個關於可調用的StackOverflow答案可以幫助您理解上述內容。

更詳細:

雖然答案被接受,但由於與格倫的有趣討論並且有幾分鍾免費,我想擴大我的答案:

# 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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM