Mock class 用于具有动态属性的 isinstance 检查
Mock class used in isinstance checks which has dynamic attributes
一些 classes 在 class 级别(在 __init__
或任何其他函数之外)定义它们的属性(又名字段)。一些 classes 在它们的 __init__
函数中甚至从其他函数中定义它们。一些 class 使用这两种方法。
class MyClass(object):
foo = 'foo'
def __init__(self, *args, **kwargs):
self.bar = 'bar'
问题是,当您使用 dir
时,如果您传入 class.
的实例,它只包含 '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
但我想在 [=22] 中将其用作 spec
设置(以通过 isinstance
检查) =] 调用单元测试。
@mock.patch('mypackage.MyClass', spec=MyClass)
def test_thing_that_depends_on_MyClass(self, executeQueryMock):
# uses 'thing' here, which uses MyClass.bar ...
这样做会导致:
AttributeError: Mock object has no attribute 'bar'
这是有道理的,因为 mock docs 说:
spec: This can be either a list of strings or an existing object (a class or instance) that acts as the specification for the mock object. If you pass in an object then a list of strings is formed by calling dir on the object (excluding unsupported magic attributes and methods). Accessing any attribute not in this list will raise an 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' object is not callable
我真的不在乎严格限制functions/attributes我允许访问的内容;我实际上想要正常的 MagicMock 行为,它可以让你在没有 AttributeError
的情况下调用任何东西。似乎使用 spec
使这个变得严格,即使我只是使用 spec
通过 isinstance
检查。
问题:
我如何正确地模拟 isinstance
检查中使用的 class 并具有未在 class 级别定义的属性?
鉴于您只想通过 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
检查,就像规范模拟一样。
一些 classes 在 class 级别(在 __init__
或任何其他函数之外)定义它们的属性(又名字段)。一些 classes 在它们的 __init__
函数中甚至从其他函数中定义它们。一些 class 使用这两种方法。
class MyClass(object):
foo = 'foo'
def __init__(self, *args, **kwargs):
self.bar = 'bar'
问题是,当您使用 dir
时,如果您传入 class.
'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
但我想在 [=22] 中将其用作 spec
设置(以通过 isinstance
检查) =] 调用单元测试。
@mock.patch('mypackage.MyClass', spec=MyClass)
def test_thing_that_depends_on_MyClass(self, executeQueryMock):
# uses 'thing' here, which uses MyClass.bar ...
这样做会导致:
AttributeError: Mock object has no attribute 'bar'
这是有道理的,因为 mock docs 说:
spec: This can be either a list of strings or an existing object (a class or instance) that acts as the specification for the mock object. If you pass in an object then a list of strings is formed by calling dir on the object (excluding unsupported magic attributes and methods). Accessing any attribute not in this list will raise an 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' object is not callable
我真的不在乎严格限制functions/attributes我允许访问的内容;我实际上想要正常的 MagicMock 行为,它可以让你在没有 AttributeError
的情况下调用任何东西。似乎使用 spec
使这个变得严格,即使我只是使用 spec
通过 isinstance
检查。
问题:
我如何正确地模拟 isinstance
检查中使用的 class 并具有未在 class 级别定义的属性?
鉴于您只想通过 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
检查,就像规范模拟一样。