The Problem
Using mock.patch
with autospec=True
to patch a class is not preserving attributes of instances of that class.
The Details
I am trying to test a class Bar
that instantiates an instance of class Foo
as a Bar
object attribute called foo
. The Bar
method under test is called bar
; it calls method foo
of the Foo
instance belonging to Bar
. In testing this, I am mocking Foo
, as I only want to test that Bar
is accessing the correct Foo
member:
import unittest
from mock import patch
class Foo(object):
def __init__(self):
self.foo = 'foo'
class Bar(object):
def __init__(self):
self.foo = Foo()
def bar(self):
return self.foo.foo
class TestBar(unittest.TestCase):
@patch('foo.Foo', autospec=True)
def test_patched(self, mock_Foo):
Bar().bar()
def test_unpatched(self):
assert Bar().bar() == 'foo'
The classes and methods work just fine (test_unpatched
passes), but when I try to Foo in a test case (tested using both nosetests and pytest) using autospec=True
, I encounter "AttributeError: Mock object has no attribute 'foo'"
19:39 $ nosetests -sv foo.py
test_patched (foo.TestBar) ... ERROR
test_unpatched (foo.TestBar) ... ok
======================================================================
ERROR: test_patched (foo.TestBar)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/mock.py", line 1201, in patched
return func(*args, **keywargs)
File "/home/vagrant/dev/constellation/test/foo.py", line 19, in test_patched
Bar().bar()
File "/home/vagrant/dev/constellation/test/foo.py", line 14, in bar
return self.foo.foo
File "/usr/local/lib/python2.7/dist-packages/mock.py", line 658, in __getattr__
raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'foo'
Indeed, when I print out mock_Foo.return_value.__dict__
, I can see that foo
is not in the list of children or methods:
{'_mock_call_args': None,
'_mock_call_args_list': [],
'_mock_call_count': 0,
'_mock_called': False,
'_mock_children': {},
'_mock_delegate': None,
'_mock_methods': ['__class__',
'__delattr__',
'__dict__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__'],
'_mock_mock_calls': [],
'_mock_name': '()',
'_mock_new_name': '()',
'_mock_new_parent': <MagicMock name='Foo' spec='Foo' id='38485392'>,
'_mock_parent': <MagicMock name='Foo' spec='Foo' id='38485392'>,
'_mock_wraps': None,
'_spec_class': <class 'foo.Foo'>,
'_spec_set': None,
'method_calls': []}
My understanding of autospec is that, if True, the patch specs should apply recursively. Since foo is indeed an attribute of Foo instances, should it not be patched? If not, how do I get the Foo mock to preserve the attributes of Foo instances?
NOTE:
This is a trivial example that shows the basic problem. In reality, I am mocking a third party module.Class -- consul.Consul
-- whose client I instantiate in a Consul wrapper class that I have. As I don't maintain the consul module, I can't modify the source to suit my tests (I wouldn't really want to do that anyway). For what it's worth, consul.Consul()
returns a consul client, which has an attribute kv
-- an instance of consul.Consul.KV
. kv
has a method get
, which I am wrapping in an instance method get_key
in my Consul class. After patching consul.Consul
, the call to get fails because of AttributeError: Mock object has no attribute kv.
Resources Already Checked:
http://mock.readthedocs.org/en/latest/helpers.html#autospeccing http://mock.readthedocs.org/en/latest/patch.html