嘲笑父类

jul*_*enc 2 python mocking pytest

我有一个Parent被很多ChildX类继承的类。

class Parent(object):  # the class that should be mocked
    def __init__(self):
        assert False  # should never happen, because we're supposed to use the mock instead

class Child1(Parent):
    def method_1(self):
        return 3

class MockParent(object):  # this one should replace Parent
    def __init__(self):
        assert True
Run Code Online (Sandbox Code Playgroud)

我有一个与 pytest 一起运行的测试套件,我想确保在这些测试期间,每次 aChildX被实例化时,实例都会调用MockParent的方法而不是 的方法Parent(我简化了上面的示例,__init__方法不是唯一相关的)。

目前,我所能做的就是ChildX一个一个地修补每个类:

class FixturePatcher(object):
    def __init__(self, klass):
        self.klass = klass
        self.patcher = None

    def __enter__(self):
        self.patcher = mock.patch.object(self.klass, '__bases__', (MockParent,))
        return self.patcher.__enter__()

    def __exit__(self, *_, **__):
        self.patcher.is_local = True

@pytest.fixture()
def mock_parent_on_child1():
    with FixturePatcher(Child1):
        return Child1()

def test_mock_child1(mock_parent_on_child1):
    assert mock_parent_on_child1.method_1() == 3
Run Code Online (Sandbox Code Playgroud)

但是我有很多ChildX类,有时ChildY在 的方法中被实例化和使用ChildZ,所以我不能全部修补它们。

我试过很多东西来替代Parent通过MockParent,但他们没有工作。以下是我的一些失败尝试:

@mock.patch('src.parent.Parent', MockParent)
def test_mock_parent():
    assert Child1().method_1() == 3  # Parent's __init__ method is still called

@mock.patch('src.parent.Parent.__getattribute__', lambda self, name: MockParent.__getattribute__(self, name))
def test_mock_parent():
    assert Child1().method_1() == 3  # same results here

def test_mock_parent(monkeypatch):
    monkeypatch.setattr('src.parent.Parent', MockParent)
    assert Child1().method_1() == 3  # and still the same here
Run Code Online (Sandbox Code Playgroud)

这甚至可能吗?我使用python2.7和上最新版本的pytestmock

zvo*_*one 6

我想不出一种方法可以ParentMockParent任何使用它的地方替换它,但是您可以使用monkeypatchParent的方法,如果这是您所追求的:

class Parent(object):
    def my_method(self):
         print 'my method'

class Child(Parent):
    def run(self):
         self.my_method()

child = Child()

child.run()  # prints: my method

### Apply the monkeypatch: ###

def my_method(self):
    print 'something else'

Parent.my_method = my_method

child.run()  # prints: something else
Run Code Online (Sandbox Code Playgroud)

您还可以以相同的方式使用Parent来自 的方法对每个方法进行猴子修补MockParent。这几乎与您更改从Parent.

编辑:

事实上,如果您搜索 的所有现有子类Parent,修补它们,并Parent在其模块中对定义进行猴子修补,那么您可能也可以完全按照您的要求执行,以便将来的类也更新:

for child_class in Parent.__subclasses__():
    mock.patch.object(child_class, '__bases__', (MockParent,))

parents_module.Parent = MockParent
Run Code Online (Sandbox Code Playgroud)

我认为这仍然会错过一些特殊情况,因此对每种方法进行猴子修补可能会更好。