修补类产生" AttributeError:Mock对象没有属性"访问实例属性时

时间:2015-07-29 19:47:21

标签: python unit-testing attributes mocking python-unittest

问题
使用mock.patchautospec=True修补类并不保留该类实例的属性。

详情
我正在尝试测试一个类Bar,它将类Foo的实例实例化为名为Bar的{​​{1}}对象属性。测试中的foo方法称为Bar;它调用属于bar的{​​{1}}实例的方法foo。在测试中,我正在嘲笑Foo,因为我只想测试Bar是否正在访问正确的Foo成员:

Bar

类和方法工作得很好(Foo次传递),但是当我尝试在使用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' 的测试用例(使用nosetests和pytest测试)中进行Foo时,我会遇到" AttributeError:Mock对象没有属性' foo'"

test_unpatched

事实上,当我打印autospec=True时,我可以看到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' 不在儿童或方法列表中:

mock_Foo.return_value.__dict__

我对autospec的理解是,如果为True,补丁规范应该递归应用。由于foo确实是Foo实例的一个属性,它是不是应该修补?如果没有,我如何让Foo mock保留Foo实例的属性?

注意:
这是一个显示基本问题的简单示例。实际上,我正在嘲笑第三方模块。类 - foo - 其客户端我在Consul包装器类中实例化。因为我不维护领事模块,所以我无法修改源代码以适合我的测试(无论如何我都不想这样做)。对于它的价值,{'_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': []} 会返回一个consul客户端,该客户端具有属性consul.Consul - consul.Consul()的实例。 kv有一个方法consul.Consul.KV,我将其包含在Consul类的实例方法kv中。修补get后,由于AttributeError,调用get失败:Mock对象没有属性kv。

已检查资源:

http://mock.readthedocs.org/en/latest/helpers.html#autospeccing http://mock.readthedocs.org/en/latest/patch.html

1 个答案:

答案 0 :(得分:35)

不,自动指定不能模拟原始类(或任何其他方法)的__init__方法中设置的属性。它只能模拟静态属性,即可在课堂上找到的所有内容。

否则,mock必须首先创建一个你试图用mock替换的类的实例,这不是一个好主意(想想在实例化时创建大量实际资源的类)。 p>

自动编译模拟的递归性质仅限于那些静态属性;如果foo是类属性,则访问Foo().foo将返回该属性的自动推文模拟。如果您的Spam属性类eggs属于Ham类型的对象,那么Spam.eggs的模拟将是Ham的自动模拟模拟} class。

明确<{3}} 涵盖了这一点:

  

更严重的问题是,例如,在__init__方法中创建属性并且根本不存在于类中是常见的。 autospec无法了解任何动态创建的属性,并将api限制为可见属性。

您应该自己设置缺少的属性:

@patch('foo.Foo', autospec=TestFoo)
def test_patched(self, mock_Foo):
    mock_Foo.return_value.foo = 'foo'
    Bar().bar()

或创建Foo类的子类以进行测试,将属性添加为类属性:

class TestFoo(foo.Foo):
    foo = 'foo'  # class attribute

@patch('foo.Foo', autospec=TestFoo)
def test_patched(self, mock_Foo):
    Bar().bar()