I am playing with patches and mocks and trying to understand the difference
between patch of the method of a class and patch of the method of patched class.
In both cases I create class instance and try to patch the bound method by bounded one with
the explicit directive autospec=True
. However, in first case I successfully
patch the method with expected behaviour whereas in second case the
patched object behaves unexpected.
It demonstrates the following code:
from mock import patch
class B(object):
def zero(self):
return 0
class A(object):
def __init__(self):
self.y = B()
self.i = self.y.zero()
def mocked_zero(*args, **kwargs):
return 1
a = A()
assert a.i == 0 # expected
with patch('__main__.B.zero', autospec=True) as mocked_B_zero:
mocked_B_zero.side_effect = mocked_zero
a = A()
assert a.i == 1 # expected
mocked_B_zero.assert_called_once_with(a.y) # expected
with patch('__main__.B', autospec=True) as mocked_B:
instance = mocked_B.return_value
instance.zero.side_effect = mocked_zero
# I placed the following line but it changes nothing
instance.zero.autospec = True
a = A()
assert a.i == 1 # expected
instance.zero.assert_called_once_with() # why does it pass?
I expected the directive autospec=True
will do the work and instance.zero.assert_called_once_with(a.y)
will pass instead.
It should be noticed that directive autospec=True
changes the mocked_b
object.
with patch('__main__.B') as mocked_B:
creates an object mocked_B
: <MagicMock name='B' id='...'>
. Whereas
with patch('__main__.B', autospec=True) as mocked_B:
creates a different object mocked_B
: <MagicMock name='B' spec='B' id='...'>
The is a great explanation of meaning of the keyword argument autospec=True
in case of patching class method:
Mock's autospec injects a wrong argument into a called function
https://www.toptal.com/python/an-introduction-to-mocking-in-python (see comments)
but it remains unclear the meaning of the directive autospec=True
in case of
class patching.
So, I have several questions:
- What does directive
autospec=True
means in case of entire class patching? - Why
instance.zero.assert_called_once_with()
passes in case of method patching of the patched class? - Is it possible to patch a bound method by a bounded one in the second case so that
instance.zero.assert_called_once_with(a.y)
passes?
UPDATE
The situation where the need to patch both class and one of its method appears.
I need to test a class, say A
, that uses in its implementation another class, say B
. Simplified it looks like this:
class B(object):
def __init__(self, config):
pass
def get_items(self): # instead of 'zero' function
while True: # infinite loop
item = self.request_to_db(some_params)
yield item
class A(object):
def __init__(self, config)
self.b = B(config)
def run(self):
for item in self.b.get_items():
do_some_action(item)
I need
- to check that instantiating the class
A
likea = A(config)
the classB
was called with parameterconfig
. - to check that calling
a.run()
the method get_items was called and is placed into the loop.
For this purpose I want
- to patch the entire class
B
with some mock object - to patch the method
get_items
with some specially constructed mock object (side_effect option is used here) raisingStopIteration
exception after defined number of iterations.
Of course, do_some_action
method is also patched.