How do I patch an object so that all methods are mocked except one?
我有一个入口点函数,它在一个对象上调用它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Thing(object): def main(self): self.alpha() self.bravo() def alpha(self): self.charlie() def bravo(self): raise TypeError("Requires Internet connection!") def charlie(self): raise Exception("Bad stuff happens here!") |
这很简单,可以手动模拟:
1 2 3 | thing = Thing() thing.alpha = MagicMock() thing.bravo = MagicMock() |
我可以测试以确保alpha和bravo都被调用一次,我可以在alpha和bravo中设置副作用以确保它们被处理等等。
我担心的是,如果代码定义发生变化,并且有人将
我的计划是验证我的模拟对象不会调用其他方法而不是我应该说的方法(或者引发测试异常)。但是,如果我做这样的事情:
1 2 3 4 5 6 | MockThing = create_autospec(Thing) thing = Thing() thing.main() print thing.method_calls # [calls.main()] |
然后
编辑:用于黑客答案
好吧,我有一个非常hacky的解决方案,但我希望有一个比这更好的答案。基本上我从原始类重新绑定方法(Python绑定一个未绑定的方法)
1 2 3 4 5 6 7 | MockThing = create_autospec(Thing) thing = MockThing() thing.main = Thing.ingest.__get__(thing, Thing) thing.main() print thing.method_calls # [calls.alpha(), calls.bravo()] |
但是必须有一个比使用函数描述符更简单的解决方案!
当我做这些奇怪的事情,比如调用一个类的实际方法,我会模拟我用来调用方法静态引用:
1 2 3 4 5 | mt = Mock(Thing) Thing.main(mt) print(mt.mock_calls) [call.alpha(), call.bravo()] |
在编写测试之后,最好通过使用一些协作者将您应该模拟的内容与要测试的内容分开来进行分离:使用这些测试来引导生产代码重构,最后重构您的测试以删除这些类型的脏测试。
我有同样的问题,但我找到了一种方法,我很满意。下面的示例使用您上面列出的Thing类:
1 2 3 4 | import mock mock_thing = mock.create_autospec(Thing) mock_thing.main = lambda x: Thing.main(mock_thing, x) |
这将导致mock_thing调用属于mock_thing的实际"main"函数,但mock_thing.alpha()和mock_thing.beta()都将被称为模拟! (将x替换为您传入函数的任何参数)。
希望这对你有用!
看起来你要么让单元测试进入你的集成/功能测试,要么你担心测试除了某个单元之外的其他东西。
如果你是单元测试
此外,这是依赖注入很有意义的地方,因为你会做这样的事情:
1 2 3 4 5 6 7 8 9 | class Thing: def __init__(self, db, dangerous_thing): self.db = db self.dangerous_thing = dangerous_thing #.... def charlie(self): foxtrot = self.dangerous_thing.do_it() |
现在,您可以在测试