当测试一起运行时,所有测试中使用的外部库模拟补丁不起作用

Lar*_*ite 1 python unit-testing mocking python-unittest

我正在使用 Python 的模拟库和 unittest。我正在为一个类编写单元测试,该类在其方法之一中使用外部库的函数。根据情况,此函数返回不同的值。

假设我想测试 A 类:

from external_library import function_foo

class A(object):
...
Run Code Online (Sandbox Code Playgroud)

在我的测试类中,为了使用函数从外部库返回的值,我创建了一个补丁,定义了补丁后才导入类A。但是,我需要在我所有的测试方法中使用这个函数,并且在每个方法中它返回不同的值。

我的测试类如下:

class TestA(TestCase):

    @patch('external_library.function_foo', side_effect=[1, 2, 3])    
    def test_1(self, *patches):

       from module import class A
       obj = A()
       ...

    @patch('external_library.function_foo', side_effect=[1, 1, 2, 2, 3, 3])    
    def test_2(self, *patches):

       from module import class A
       obj = A()
       ...

    ...
Run Code Online (Sandbox Code Playgroud)

我有 10 个测试,当我同时运行所有测试时,只有 1 个(第一个)通过,其余的,我得到StopIteration错误。但是,如果我单独运行它们中的每一个,它们都会通过

我尝试with patch('external_library.function_foo', side_effect=[...])在每种方法中使用,但结果是一样的。我还尝试在setUp方法中只创建一次补丁,启动它,在每个方法中重新分配 side_effect,然后在 中停止tearDown,但没有奏效。

关于在这种情况下可能起作用的任何想法?

谢谢!

geo*_*xsh 5

需要注意的是,第二次导入模块时,它不会再次加载,您将获得与第一次导入时相同的模块对象。

当您第一次运行“test_1”时,external_library.function_foo替换为一个Mock对象,让我们将其命名为mock_a。然后你的“模块”第一次被导入,python会加载它,意思是,执行“模块”内部的代码,将名称“function_foo”绑定到“模块”命名空间中的对象“mock_a”,保存“模块”反对sys.modules. 这次测试将通过,而side_effectmock_a被消耗。

接下来是“test_2”,external_library.function_foo替换为一个Mock对象,将其命名为mock_b. 然后导入“module”,这次它不会再次加载,而是填充 from sys.modules,您将获得与“test_1”中相同的模块对象。在这个模块对象的命名空间中,名称“function_foo”仍然绑定到 object mock_a,而不是新创建的mock_b. 由于side_effectmock_a已经消耗,StopIteration错误引发。

您应该将补丁应用于查找名称的位置,而不是定义名称的位置:

@patch('module.function_foo', side_effect=[1, 2, 3])    
def test_1(self, patch):
    ...
Run Code Online (Sandbox Code Playgroud)

阅读关于更详细的说明书中的“去哪儿补丁”一节patch