Supposons que j'ai le code suivant dans un Python test d'unité:
aw = aps.Request("nv1")
aw2 = aps.Request("nv2", aw)
Existe-t-il un moyen simple d’affirmer qu’une méthode particulière (dans mon cas, aw.Clear()
) a été appelée au cours de la deuxième ligne du test? par exemple. y a-t-il quelque chose comme ça:
#pseudocode:
assertMethodIsCalled(aw.Clear, lambda: aps.Request("nv2", aw))
J'utilise Mock (qui est maintenant nittest.mock sur py3.3 +) pour cela:
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)
Pour votre cas, cela pourrait ressembler à ceci:
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 prend en charge un certain nombre de fonctionnalités utiles, y compris des méthodes pour patcher un objet ou un module, ainsi que pour vérifier que la bonne chose a été appelée, etc. etc.
Caveat emptor! (Attention à l'acheteur!)
Si vous vous trompez assert_called_with
(à assert_called_once
ou assert_called_wiht
) votre test peut toujours être exécuté, car Mock pensera que c'est une fonction fictive et ira de l'avant, sauf si vous utilisez autospec=true
. Pour plus d'informations, lisez assert_called_once: Menace ou menace .
Oui si vous utilisez Python 3.3+. Vous pouvez utiliser la fonction intégrée unittest.mock
pour affirmer la méthode appelée. Pour Python 2.6+ utiliser le backport roulant Mock
, ce qui revient au même.
Voici un exemple rapide dans votre cas:
from unittest.mock import MagicMock
aw = aps.Request("nv1")
aw.Clear = MagicMock()
aw2 = aps.Request("nv2", aw)
assert aw.Clear.called
Je ne suis au courant de rien. C'est assez simple à mettre en œuvre:
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()
Cela nécessite que l'objet lui-même ne modifie pas self.b, ce qui est presque toujours vrai.
Oui, je peux vous donner un aperçu, mais mon Python est un peu rouillé et je suis trop occupé pour expliquer en détail.
En gros, vous devez mettre un proxy dans la méthode qui appellera l'original, par exemple:
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)
Ceci réponse StackOverflow à propos de callable peut vous aider à comprendre ce qui précède.
Plus en détail:
Bien que la réponse ait été acceptée, en raison de la discussion intéressante avec Glenn et de la gratuité de quelques minutes, je souhaitais développer ma réponse:
# 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)
Vous pouvez simuler aw.Clear
Manuellement ou à l'aide d'un framework de test tel que pymox . Manuellement, vous le feriez en utilisant quelque chose comme ceci:
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
En utilisant pymox, vous le feriez comme ceci:
class MyTest(mox.MoxTestBase):
def testClear():
aw = self.m.CreateMock(aps.Request)
aw.Clear()
self.mox.ReplayAll()
aps.Request('nv2', aw)