Lia*_*avK 6 python unit-testing mocking
Mock 文档描述了一种简单而优雅的方法,可以将补丁应用于以下内容中的所有测试方法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)
Run Code Online (Sandbox Code Playgroud)
但是,我在这个方法中遇到的一个问题是,如果我想在其中一个测试方法中的一个补丁上调用stop(),那么似乎无法获得对补丁的引用object - 传递给方法的唯一东西是mock对象,在本例bar中baz是quux,narf.
我发现解决这个问题的唯一方法是转到模拟文档中描述的模式,其中实例化修补程序并在setUp方法内部启动并在方法内TestCase停止tearDown.这符合我的目的,但增加了许多额外的样板,并没有像类装饰器方法那样优雅.
有没有其他方法可以解决这个问题?
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__()
Run Code Online (Sandbox Code Playgroud)
在您的测试用例中,您可以像这样使用它:
with unpatch('foo.narf', self.test_foo):
foo.narf()
Run Code Online (Sandbox Code Playgroud)
免责声明:这是黑客攻击.