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

时间:2018-01-16 16:39:37

标签: python unit-testing mocking python-unittest

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

所以,让我说我想测试A类:

from external_library import function_foo

class A(object):
...

在我的测试类中,为了使用外部库中函数返回的值,我创建了一个补丁,并且在定义补丁后只导入了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()
       ...

    ...

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

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

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

谢谢!

1 个答案:

答案 0 :(得分:1)

需要注意的是,第二次导入模块it would not be loaded again时,您将获得与第一次导入时相同的模块对象。

当您第一次运行“test_1”时,external_library.function_fooMock对象替换,我们将其命名为mock_a。然后你的“模块”第一次导入,python将加载它,意味着,在“module”中执行代码,它将名称“function_foo”绑定到“module”命名空间中的对象“mock_a”,保存“module”对象sys.modules。这次您的测试将通过,side_effect的{​​{1}}将被消耗。

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

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

StopIteration

详细了解"Where to patch" section of the manual of patch