2

一些类在类级别(外部__init__或任何其他功能)定义它们的属性(又名字段)。一些类在它们的__init__函数中甚至从其他函数中定义它们。有些类同时使用这两种方法。

class MyClass(object):
  foo = 'foo'
  def __init__(self, *args, **kwargs):
    self.bar = 'bar'

问题是,当您使用 时dir,它仅包括'bar'您传入类的实例。

>>> dir(MyClass)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'foo']
>>> myInstance = MyClass()
>>> dir(myInstance)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo']

(滚动到最右边查看差异)

我有一种情况,我需要避免实例化MyClass,但我想将它用作单元测试调用中的spec设置(通过isinstance检查) 。mock.patch

@mock.patch('mypackage.MyClass', spec=MyClass)
def test_thing_that_depends_on_MyClass(self, executeQueryMock):
  # uses 'thing' here, which uses MyClass.bar ...

这样做会导致:

AttributeError:模拟对象没有属性“bar”

这是有道理的,因为模拟文档说:

规范:这可以是字符串列表,也可以是充当模拟对象规范的现有对象(类或实例)。如果您传入一个对象,则通过在该对象上调用 dir 来形成一个字符串列表(不包括不受支持的魔法属性和方法)。访问不在此列表中的任何属性都会引发 AttributeError。

即使我实例化MyClass了,我也会得到一个不同的错误。

@mock.patch('mypackage.MyClass', spec=MyClass())
def test_thing_that_depends_on_MyClass(self, executeQueryMock):
  # uses 'thing' here, which uses MyClass.bar ...

原因:

TypeError:“NonCallableMagicMock”对象不可调用

我并不真正关心我允许访问哪些功能/属性;我实际上想要正常的 MagicMock 行为,它可以让你在没有AttributeError. spec尽管我只是spec用来通过isinstance检查,但似乎 using使这个变得严格。

问题:

如何正确模拟这个在isinstance检查中使用并且具有未在类级别定义的属性的类?

4

1 回答 1

1

鉴于您只想通过isinstance检查,我认为最简单的解决方案是编写一个快速包装器来mock.patch.object设置__class__返回的模拟的属性。

def my_patch(obj, attr_name):
    fake_class = mock.MagicMock()
    fake_instance = fake_class.return_value  # calling a class returns an instance.
    fake_instance.__class__ = getattr(obj, attr_name)
    return mock.patch.object(obj, attr_name, fake_class)

它的用法如下mock.patch.object

@my_patch(some_module, 'MyClass')
def test_something(self, fake_my_class):
    ...

但假对象应该isinstance以与规范模拟相同的方式通过检查。

于 2015-03-11T21:07:00.970 回答