patching a class yields “AttributeError: Mock object has no attribute” when accessing instance attributes
问题使用
细节我试图测试一个类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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' |
类和方法工作得很好(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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' |
实际上,当我打印出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | {'_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': []} |
我对autospec的理解是,如果为真,补丁规范应该递归地应用。因为foo确实是foo实例的一个属性,所以不应该修补它吗?如果不是,如何获取foo mock以保留foo实例的属性?
注:这是一个显示基本问题的简单示例。实际上,我在模拟第三方模块类
已检查的资源:
http://mock.readthedocs.org/en/latest/helpers.html自动排序http://mock.readthedocs.org/en/latest/patch.html
不,autospeccing不能模拟在原始类的
否则,mock必须首先创建一个您试图用mock替换的类的实例,这不是一个好主意(想想在实例化时创建大量实际资源的类)。
然后,自动指定的模拟的递归性质仅限于这些静态属性;如果
您所阅读的文档明确涵盖了以下内容:
A more serious problem is that it is common for instance attributes to be created in the
__init__ method and not to exist on the class at all.autospec can’t know about any dynamically created attributes and restricts the api to visible attributes.
您应该自己设置缺少的属性:
1 2 3 4 | @patch('foo.Foo', autospec=TestFoo) def test_patched(self, mock_Foo): mock_Foo.return_value.foo = 'foo' Bar().bar() |
或者为测试目的创建
1 2 3 4 5 6 | class TestFoo(foo.Foo): foo = 'foo' # class attribute @patch('foo.Foo', autospec=TestFoo) def test_patched(self, mock_Foo): Bar().bar() |