Убедитесь, что метод был вызван в Python unit test
Предположим, что у меня есть следующий код в Python unit test:
aw = aps.Request("nv1")
aw2 = aps.Request("nv2", aw)
Есть ли простой способ утверждать, что в ходе второй строки теста был вызван конкретный метод (в моем случае aw.Clear()
)? например есть что-то вроде этого:
#pseudocode:
assertMethodIsCalled(aw.Clear, lambda: aps.Request("nv2", aw))
Ответы
Ответ 1
Я использую Mock (который теперь является unittest.mock на py3. 3+) для этого:
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 будет считать, что это assert_called_with
assert_called_once
и будет assert_called_wiht
, если вы не используете autospec=true
. Для получения дополнительной информации прочитайте assert_called_once: Угроза или угроза.
Ответ 2
Да, если вы используете Python 3. 3+. Вы можете использовать встроенный unittest.mock
для 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
Ответ 3
Я не знаю ничего встроенного. Это довольно просто реализовать:
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, что почти всегда верно.
Ответ 4
Да, я могу дать вам схему, но мой 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)
Этот fooobar.com/questions/28753/... о вызываемом может помочь вам понять выше.
Подробнее:
Хотя ответ был принят, из-за интересного обсуждения с Гленном и свободного времени на несколько минут, я хотел увеличить свой ответ:
# 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)
Ответ 5
Вы можете измазать 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)