使用Python进行模拟时避免使用冗余@patch

Tra*_*rks 12 python patch mocking code-duplication

来自静态编程语言背景,我想知道如何最好地在Python中进行模拟.我习惯于依赖注入.在测试中,模拟被创建并传递给被测系统(SUT).但是,看看Mock和Python的其他模拟框架,它似乎是类型/函数/等.模块中的模块在逐个测试的基础上被替换.

特别是,对于Mock,每个单元测试的顶部你会说出@patch('some.type.in.the.module.under.test')每种类型/功能/等.你想嘲笑 在测试的一生中,这些东西被嘲笑,然后它们被还原.不幸的是,在测试中,灯具非常接近相同,你最终会@patch一遍又一遍地重复你的es.

我想要一种方法来跨单元测试共享补丁集合.我还希望以可组合的方式对夹具进行调整.我可以使用上下文管理器而不是装饰器.

ayc*_*dee 10

您可以修补流向该类中每个方法的测试类.然后你可以从超类继承并使用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
Run Code Online (Sandbox Code Playgroud)

这不像你想象的那么普遍.因为您需要在对象的确切位置对其进行修补.你不修补'sys.stdout',你修补'my_dir.my_module.sys.stdout'.所以它在测试特定模块时才真正有用.但是为了测试那个特定的模型,你当然只需要一个补丁修饰器.


小智 5

我最近遇到了类似的情况,但更极端。我的顶级模块之一必须模拟多个存储库、提供程序和逻辑库。这导致需要@patch7 个组件进行大量单元测试。我想避免大量重复的测试代码,所以这是我的解决方案,效果很好:

@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
Run Code Online (Sandbox Code Playgroud)

类上的s@mock在运行时应用于每个测试,并将所有补丁传递到 *mocks 参数中。需要记住的重要一点是顺序 - 我将索引注释放在代码中,以使其在我的脑海中保持清晰。

希望这可以帮助。