使用Python进行模拟时避免使用冗余@patch
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Python进行模拟时避免使用冗余@patch相关的知识,希望对你有一定的参考价值。
来自静态编程语言背景,我想知道如何最好地在Python中进行模拟。我习惯于依赖注入。在测试中,模拟被创建并传递给被测系统(SUT)。但是,看看Mock和Python的其他模拟框架,它似乎是类型/函数/等。在逐个测试的基础上更换模块中的模块。
特别是,对于Mock,在每个单元测试的顶部,你会说每种类型/功能/等的@patch('some.type.in.the.module.under.test')
。你想嘲笑在测试的一生中,这些东西被嘲笑,然后它们被还原。不幸的是,在测试中,夹具非常接近,你最终会一遍又一遍地重复你的@patch
es。
我想要一种方法来跨单元测试共享补丁集合。我还希望以可组合的方式对夹具进行调整。我可以使用上下文管理器而不是装饰器。
您可以修补流向该类中每个方法的测试类。然后你可以从超类继承并使用setUp和tearDown方法。
import unittest
@patch('some.type.in.the.module.under.test')
class MySuperTestCase(unittest.TestCase):
pass
class MyActualTestCase(MySuperTestCase):
def test_method(self, mock_function)
mock_function.return_value = False
这不像你想象的那么普遍。因为您需要在对象的确切位置对其进行修补。你不修补'sys.stdout',你修补'my_dir.my_module.sys.stdout'。所以它在测试特定模块时才真正有用。但是为了测试那个特定的模型,你当然只需要一个补丁修饰器。
我最近遇到了类似的情况但更极端。我的一个顶级模块必须模拟出几个存储库,提供程序和逻辑库。这导致了@patch
7组件所需的大量单元测试。我想避免大量重复的测试代码,所以这里是我的解决方案,效果很好:
@mock.patch('module.blah1.method1') # index: 6
@mock.patch('module.blah1.method2') # index: 5
@mock.patch('module.blah2.method1') # index: 4
@mock.patch('module.blah2.method2') # index: 3
@mock.patch('module.blah2.method3') # index: 2
@mock.patch('module.blah3.method1') # index: 1
@mock.patch('module.blah4.method1') # index: 0
class TestsForMyCode(unittest.TestCase):
def test_first_test(self, *mocks):
# Arrange
# setup mocks for only the ones that need specific mocked behaviours
# idx 2 patches module.blah2.method3
mocks[2].return_value = 'some value'
# Act
target = sut()
result = target.do_something()
# Assert
assert result is False
def test_second_test(self, *mocks):
# Arrange
# setup mocks for only the ones that need specific mocked behaviours
# idx 0 patches module.blah4.method1
mocks[0].return_value = 'another value'
# idx 4 patches module.blah2.method1
mocks[4].return_value = 'another value'
# Act
target = sut()
result = target.do_something_else()
# Assert
assert result is True
类上的@mock
s在运行时应用于每个测试,并将所有补丁传递到* mocks参数。要记住的重要事项是排序 - 我将索引注释放在我的代码中,以便将它直接放在我的脑海中。
希望这可以帮助。
我不保证这在语法上是正确的,因为我无法测试它,但是这里是:
COMMON_FUNCTIONS = ('some.type.in.the.module.under.test', 'and.others')
def common_patches(f):
for item in COMMON_FUNCTIONS:
f = patch(item)(f)
现在应用它:
@common_patches
def something():
pass # it will be decorated by all the patches in the function
我也会推荐装饰器,因为你可以避免多余的补丁。而且不仅如此,使用参数化装饰器,您可以控制每个装饰器的自定义装置。例:
def patch_example(custom_value=None):
def _patch(test_func):
@mock.patch('some.type.in.the.module.under.test')
def _patch_it(mocked_function):
mocked_function = custom_value
return test_func(self)
return wraps(test_func)(_patch_it)
return _patch
class ExampleTestCase(object):
@patch_example(custom_value='new_value')
def test_method_1(self):
# your test logic here, with mocked values already defined in decorator
@patch_example(custom_value='new_value_2')
def test_method_2(self):
# your test logic here, with mocked values already defined in decorator
以上是关于使用Python进行模拟时避免使用冗余@patch的主要内容,如果未能解决你的问题,请参考以下文章
Python 联合 - 在另一个装饰器中收集多个 @patch 装饰器