如何创建一个行为类似于抽象类的子类的模拟

问题描述 投票:0回答:1

我正在尝试从头开始创建可以通过测试

issubclass(class_mock, base_class)
的模拟,其中基类是从
abc.ABC
派生的抽象类。在你问问题之前,我会回答为什么我要尝试这样做。
我有一个内部包,其中包含一个基类和一组正确实现抽象接口的子类。此外,我有一个可以实例化子类的工厂类。工厂的构建方式是这样的,它可以检查自己的包并可以访问现有的子类。工厂应该始终与派生类和基类(约束)位于同一个包中。我想你猜到我实际上正在测试工厂......但是,由于子类可以在数量、名称或包名称等方面发生变化,因此我无法实现直接引用实际的正确单元测试cub-classes(因为它引入了耦合)并且我需要模拟。
问题是我没有成功为从抽象类派生的类创建满足上述条件的模拟。我能够实现的是从另一个非抽象类派生的类。
这是更具体地说明问题的代码。

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 代码时,一切都变得更加复杂,我试图跟踪错误消息何时生成。不幸的是,我没能理解为什么它实际上不起作用。

python unit-testing mocking abstract-class
1个回答
0
投票

这是因为抽象类的

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

演示:https://ideone.com/IOmOY1

© www.soinside.com 2019 - 2024. All rights reserved.