Mock class used in isinstance checks which has dynamic attributes
有些类在类级别(在
1 2 3 4 | class MyClass(object): foo = 'foo' def __init__(self, *args, **kwargs): self.bar = 'bar' |
问题是,当您使用
1 2 3 4 5 | >>> 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'] |
(滚动到最右边查看差异)
我有一种情况,我需要避免实例化
1 2 3 | @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'
这是有道理的,因为模拟文档说:
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.
即使我实例化了
1 2 3 | @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
我真的不在乎对哪些函数/属性进行严格的访问;我实际上想要正常的magicMock行为,它允许您在不使用
我如何正确地模拟这个类,这个类在
考虑到您只想通过
1 2 3 4 5 | 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) |
它的用法与
1 2 3 | @my_patch(some_module, 'MyClass') def test_something(self, fake_my_class): ... |
但是这个伪对象应该通过