Mock documentation描述了一种简单而优雅的方法,可以将补丁应用于TestCase
内的所有测试方法:
@patch('foo.bar')
@patch('foo.baz')
@patch('foo.quux')
@patch('foo.narf')
class FooTest(TestCase):
def test_foo(self, bar, baz, quux, narf):
""" foo """
self.assertTrue(False)
然而,我遇到过这个方法的一个问题是,如果我想在其中一个测试方法中的一个补丁上调用stop(),那么就不会出现这种情况。无论如何都要获取对patcher对象的引用 - 传递给方法的唯一方法是模拟对象,在本例中为bar
,baz
,quux
,{{1 }}。
我发现解决此问题的唯一方法是转到模拟文档中描述的模式,其中实例化的修补程序在narf
的{{1}}方法中启动并且停在setUp
方法内。这符合我的目的,但增加了许多额外的样板,并没有像类装饰器方法那样优雅。
还有其他方法可以解决这个问题吗?
答案 0 :(得分:7)
<强> 1 强>
假设您要暂时恢复方法中的foo.narf
。 foo.narf
在装饰函数的上下文中是MagicMock
对象。该对象具有_mock_wraps
属性,在调用mock时将调用该属性!因此,在您的模块顶部_narf = foo.narf
和测试用例foo.narf._mock_wraps = _narf
。
问题是这只会传递给真正的函数,而不是实际交换它,这意味着一些测试用例将失败(例如,如果它们依赖于函数对象实际上是“本身”)。如果你的模拟有其他属性,可以干扰(我没有测试太多),因为对_mock_wraps()
的直通调用位于首先考虑其他属性的方法的底部。模拟。
<强> 2 强>
patch()
装饰器涉及每个patcher
(每个方法单独的副本)被添加到名为patchings
的列表中,该列表是方法本身的字段。即您可以self.test_foo.patchings
访问此列表,然后查找所需的列表。
但是,当您使用start()
作为装饰器时,实际上并未调用stop()
和patch()
,并且一旦您开始进入并更改它,行为就会变得棘手。所以我写了这个上下文管理器。
class unpatch:
def __init__(self, name, method):
compare = patch(name)
self.patcher = next((
p for p in method.patchings
if p.target == compare.getter()
and p.attribute == compare.attribute
), None)
if self.patcher is None:
raise ValueError(name)
def __enter__(self):
self.patcher.__exit__()
def __exit__(self, *exc_info):
self.patcher.__enter__()
在测试用例中,您可以像这样使用它:
with unpatch('foo.narf', self.test_foo):
foo.narf()
免责声明:这是黑客攻击。