Убедитесь, что метод был вызван в Python unit test - программирование
Подтвердить что ты не робот

Убедитесь, что метод был вызван в 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))
4b9b3361

Ответ 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)