嘲笑父类

时间:2016-09-03 13:06:54

标签: python mocking pytest

我有一个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 类继承。

ChildX

我有一个使用pytest运行的测试套件,我想确保在这些测试期间,每次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 课程:

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 ,但没有一个能够奏效。以下是我失败的一些尝试:

pytest

这甚至可能吗?我正在使用python2.7以及mock+的最新版本。

2 个答案:

答案 0 :(得分:2)

我想不出用Parent替换MockParent到处使用它的地方的方法,但你可以用monkeypatch Parent的方法代替,如果这就是你的意思后:

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

您也可以使用Parent中的方法以同样的方式对MockParent的每个方法进行单一操作。这与您更改从Parent继承的所有内容的父类几乎相同。

修改

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

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

parents_module.Parent = MockParent

我认为这仍然会遗漏一些特殊的情况,所以monkeypatching每个方法可能更好。

答案 1 :(得分:1)

不,这是不可能的。 Child1和Parent的定义 - 包括一个来自另一个的继承 - 在导入它们所在的模块时立即执行。在此之前没有机会进入并更改继承层次结构。

您可以做的最好的事情是将定义移动到某种工厂函数,这可以根据调用的内容动态定义Child1的父级。