How to mock a related method for introspection
I would like to test the following code
import weakref
class WeakBoundMethod:
"""
Wrapper around a method bound to a class instance. As opposed to bare
bound methods, it holds only a weak reference to the `self` object,
allowing it to be deleted.
This can be useful when implementing certain kinds of systems that
manage callback functions, such as an event manager.
"""
def __init__(self, meth):
"""
Initializes the class instance. It should be ensured that methods
passed through the `meth` parameter are always bound methods. Static
methods and free functions will produce an `AssertionError`.
"""
assert (hasattr(meth, '__func__') and hasattr(meth, '__self__')),\
'Object is not a bound method.'
self._self = weakref.ref(meth.__self__)
self._func = meth.__func__
def __call__(self, *args, **kw):
"""
Calls the bound method and returns whatever object the method returns.
Any arguments passed to this will also be forwarded to the method.
In case an exception is raised by the bound method, it will be
caught and thrown again to the caller of this `WeakBoundMethod` object.
Calling this on objects that have been collected will result in
an `AssertionError` being raised.
"""
assert self.alive(), 'Bound method called on deleted object.'
try:
return self._func(self._self(), *args, **kw)
except Exception as e:
raise e
def alive(self):
"""
Checks whether the `self` object the method is bound to has
been collected.
"""
return self._self() is not None
I was thinking about using mock from the standard library to check if the __call__ method is calling the required arguments. The problem I'm running into is that if I create a Mock object for the associated method, it doesn't have a __self__ or __func__ attribute. So I tried the following code:
class TestWeakBoundMEthod(unittest.TestCase):
def setUp(self):
self.bm = Mock(name='bound method')
self.bm.__self__ = Mock(name='self')
self.bm.__func__ = Mock(name='func')
def test_call(self):
wbm = events.WeakBoundMethod(self.bm)
wbm()
self.bm.__func__.assert_called_with(self.bm.__self__)
wbm(1, 2, 3)
self.bm.__func__.assert_called_with(self.bm.__self__, 1, 2, 3)
It works, but I feel like I'm not testing well. I am using too much knowledge of how the WeakBoundMethod class works instead of checking the actual result.
Is there a better way to trick the associated method? Should I make a Dummy class using a dummy method?
source to share
First of all: if you are using python-3.4 why not use the WeakMethod of weakref instead?
A mock framework is powerful anyway, but sometimes it can be over-the-top and make you know too much about what you want to test: a side effect can be that your test is too tightly implementation-related.
In your case, it is better to use a real object to test it. A very complete test of your WeakBoundMethod could be as follows:
class TestWeakMethod(unittest.TestCase):
def test_call(self):
class C():
def c(self,*args,**kwargs):
return (args,kwargs)
c=C()
wc = WeakBoundMethod(c.c)
self.assertEqual(((1,2,3),{"a":11,"b":"33"}), wc(1,2,3,a=11,b="33"))
def test_just_bound_method(self):
c=object()
self.assertRaises(Exception, WeakBoundMethod, c)
self.assertRaises(Exception, WeakBoundMethod, object)
def f():
pass
self.assertRaises(Exception, WeakBoundMethod, f)
def test_is_really_weak(self):
class C():
DEAD=False
def c(self,*args,**kwargs):
return (args,kwargs)
def __del__(self):
C.DEAD = True
c=C()
wc = WeakBoundMethod(c.c)
del c
self.assertTrue(C.DEAD)
self.assertRaises(Exception, wc)
I hope it's clear enough: I like the mock
framework and use it quite heavily, but you shouldn't use it unless it's hard to create a real object that can sense and communicate what you want to test.
source to share