我正在尝试从头开始创建可以通过测试
issubclass(class_mock, base_class)
的模拟,其中基类是从abc.ABC
派生的抽象类。在你问问题之前,我会回答为什么我要尝试这样做。import unittest.mock
import inspect
import abc
class A:
pass
class B(A):
pass
class TestSubClass(unittest.TestCase):
def test_sub_class(self):
b_class_mock = self._create_class_mock("B", A)
print(isinstance(b_class_mock, type))
print(inspect.isclass(b_class_mock))
print(issubclass(b_class_mock, A))
@staticmethod
def _create_class_mock(mock_name, base_class):
class_mock = unittest.mock.MagicMock(spec=type(base_class), name=mock_name)
class_mock.__bases__ = (base_class,)
return class_mock
所以,对于这段代码来说,一切都没问题。它根据需要打印 3 True。
但是,只要类
A
被定义为抽象 (class A(abc.ABC)
),最后一个测试就会失败,并显示错误,表明该模拟不是类,即使前 2 个测试的说法相反。abc.ABCMeta
的实现,发现 __subclasscheck__
被覆盖了。我试图了解其背后的过程,但当我到达 C 代码时,一切都变得更加复杂,我试图跟踪错误消息何时生成。不幸的是,我没能理解为什么它实际上不起作用。
这是因为抽象类的
issubclass
函数的实现对第一个参数进行了硬编码验证,用于检查第一个参数是否具有 type
类型:
if (!PyType_Check(subclass)) {
PyErr_SetString(PyExc_TypeError, "issubclass() arg 1 must be a class");
return NULL;
}
因此,尽管实现了所有规范以像类一样嘎嘎,模拟对象仍然不被抽象类的
issubclass
实现视为类。
为了满足
issubclass
的硬编码验证,您可以创建一个像模拟对象一样嘎嘎叫的代理类:
def _create_class_mock(cls):
class _ABCMockMeta(abc.ABCMeta):
def __getattribute__(self, name):
try:
return getattr(mock, name)
except AttributeError:
return getattr(cls, name)
mock = MagicMock(spec=cls)
return _ABCMockMeta(cls.__name__, cls.__bases__, {})
这样:
class A(abc.ABC):
pass
class B(A):
pass
def test_sub_class():
b_class_mock = _create_class_mock(B)
print(isinstance(b_class_mock, type))
print(inspect.isclass(b_class_mock))
print(issubclass(b_class_mock, A))
test_sub_class()
输出:
True
True
True