Python iInstance检查中使用的具有动态属性的模拟类
有些类在类级别(在Python iInstance检查中使用的具有动态属性的模拟类,python,python-2.7,python-unittest,python-mock,Python,Python 2.7,Python Unittest,Python Mock,有些类在类级别(在\uuuuu init\uuuuuu>或任何其他函数之外)定义它们的属性(也称为字段)。有些类在其\uuuu init\uuu函数中定义它们,甚至从其他函数中定义它们。有些类同时使用这两种方法 class MyClass(object): foo = 'foo' def __init__(self, *args, **kwargs): self.bar = 'bar' 问题是,当您使用dir时,如果传入类的实例,它只包括'bar' >>> d
\uuuuu init\uuuuuu>或任何其他函数之外)定义它们的属性(也称为字段)。有些类在其\uuuu init\uuu
函数中定义它们,甚至从其他函数中定义它们。有些类同时使用这两种方法
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
,但我想将其用作单元测试中的mock.patch
调用中的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:模拟对象没有属性“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
检查
问题:
我如何正确地模拟这个类,这个类在isinstance
检查中使用,并且具有未在类级别定义的属性?假设您只想通过isinstance
检查,我认为最简单的解决方案是为mock.patch.object
编写一个快速包装器,用于设置返回的mock的\uuuu class\uuuu
属性
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
检查,就像规范的mock一样。通常,最好不要模拟整个类,而只是模拟类上的方法,这些方法在测试中有不必要的副作用。这可能吗?@mgilson不幸的是,测试中的“thing”在其函数中实例化了一个新的MyClass
实例,这正是我试图模拟的。这可能通过依赖项注入来解决,但这是我试图编写一些测试的遗留代码,因此我一直在避免重构,直到我有一个好的回归套件可以依赖。但我的问题是,为什么需要模拟整个类?为什么不嘲笑它有副作用的部分呢?它们是在\uuuu init\uuuuu
中显式出现的还是什么?另外,您可以在MagicMock
上设置\uuu class\uuuu
属性,允许它通过isinstance
检查,但我不确定这有多大帮助。是的,有副作用的部分是MyClass.\uu init()
@mgilson我最终重新评估了模拟整个MyClass
类的需要。我将一些副作用从`\uuu init\uuu_u_u_u_u_u_u_u_u_u_u_u_u_u_u_u_u_u_u_u_u_。谢谢你提醒我重新思考我是如何做到这一点的。我尝试了这个解决方案,它似乎可以通过我的isinstance
检查,但我遇到了一些其他问题。这不是我最终解决问题的方式,但我会给你一个公认的答案来帮助我。谢谢