Python mock.patch 装饰器影响同一测试套件中的其他单元测试



我为main_function编写了一个单元测试,并断言它使用类的实例调用函数get_things其中,并以补丁作为参数进行模拟:

@patch("get_something")
@patch("MyClass.__new__")
def test(self, mock_my_class_instance, mock_get_something):
    # Given
    dummy_my_class_instance = MagicMock()
    mock_my_class_instance.return_value = dummy_my_class_instance
    dummy_my_class_instance.get_things.return_value = {}
    # When
    main_function(parameter)
    # Then
    dummy_my_class_instance.get_things.assert_called_once_with(parameter["key1"], parameter["key2"])
    mock_get_something.assert_called_once_with(dummy_my_class_instance)

这是主要功能:

def main_function(parameter):
    properties = get_properties(parameter)
    my_class_instance = MyClass()
    list_of_things = my_class_instance.get_things(properties["key-1"], properties["key-2"])
    an_object = get_something(my_class_instance)
    return other_function(list_of_things, an_object)

它单独通过,但当与其他修补 MyClass.get_things(( 的测试一起运行时,它会失败。这是消息:

Unhandled exception occurred::'NoneType' object has no attribute 'client'

似乎补丁装饰器正在相互影响。

我试图将测试函数内的模拟创建为变量而不是装饰器,但问题仍然存在。我还尝试创建一个 tearDown(( 来停止补丁,但它似乎不起作用。

有没有办法在模拟类实例时隔离补丁或成功丢弃它们?

在这种情况下,稍微更改main_function可能更适合您,以便它更易于测试。您至少可以通过两种方式执行此操作:

您可以像这样为my_class_instance添加一个可选参数:

def main_function(parameter, my_instance = None):
    properties = get_properties(parameter)
    my_class_instance = my_instance if my_instance is not None else MyClass()
    list_of_things = my_class_instance.get_things(properties["key-1"], properties["key-2"])
    an_object = get_something(my_class_instance)
    return other_function(list_of_things, an_object)

或者,如果您不想更改实际main_function的 API,则可以创建一个更易于测试的帮助程序函数,并将main_function用作传递:

def _testable_main_function(parameter, my_instance = None):
    properties = get_properties(parameter)
    my_class_instance = my_instance if my_instance is not None else MyClass()
    list_of_things = my_class_instance.get_things(properties["key-1"], properties["key-2"])
    an_object = get_something(my_class_instance)
    return other_function(list_of_things, an_object)
def main_function(parameters):
    return _testable_main_function(parameters)

诚然,这些都需要对主函数进行代码更改,因此它们可能无法实现或特别适合您的用例。

由于类实例是一个参数而不是修补的东西,您将能够直接注入模拟对象,而无需修补类构造函数。如果没有一个完整的示例可以玩,很难说这是具体出错的地方,但我有一种预感,修补构造函数会干扰其他测试

最新更新