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
检查,但我遇到了一些其他问题。这不是我最终解决问题的方式,但我会给你一个公认的答案来帮助我。谢谢