Suppose I have the following code in a Python unit test:
aw = aps.Request("nv1")
aw2 = aps.Request("nv2", aw)
Is there an easy way to assert that a particular method (in my case aw.Clear()
) was called during the second line of the test? e.g. is there something like this:
#pseudocode:
assertMethodIsCalled(aw.Clear, lambda: aps.Request("nv2", aw))
This question is related to
python
unit-testing
Yes, I can give you the outline but my Python is a bit rusty and I'm too busy to explain in detail.
Basically, you need to put a proxy in the method that will call the original, eg:
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)
This StackOverflow answer about callable may help you understand the above.
In more detail:
Although the answer was accepted, due to the interesting discussion with Glenn and having a few minutes free, I wanted to enlarge on my answer:
# 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)
I'm not aware of anything built-in. It's pretty simple to implement:
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()
This requires that the object itself won't modify self.b, which is almost always true.
Yes if you are using Python 3.3+. You can use the built-in unittest.mock
to assert method called. For Python 2.6+ use the rolling backport Mock
, which is the same thing.
Here is a quick example in your case:
from unittest.mock import MagicMock
aw = aps.Request("nv1")
aw.Clear = MagicMock()
aw2 = aps.Request("nv2", aw)
assert aw.Clear.called
You can mock out aw.Clear
, either manually or using a testing framework like pymox. Manually, you'd do it using something like this:
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
Using pymox, you'd do it like this:
class MyTest(mox.MoxTestBase):
def testClear():
aw = self.m.CreateMock(aps.Request)
aw.Clear()
self.mox.ReplayAll()
aps.Request('nv2', aw)
Source: Stackoverflow.com