Unittest模拟补丁在循环内随机失败

时间:2020-08-19 17:58:43

标签: python python-3.x mocking python-unittest

鉴于复杂性,无法提供太多背景信息,但是我希望就为什么会发生这种情况有一些见解/发人深省的问题。

我正在测试一个将文件加载到数据库中的过程,因此我正在使用unittest.mock.patch修补数据库连接的凭据,以使用测试凭据而不是生产凭据。我们提供了一系列模拟,将其作为contextmanager的简化版本应用于此处:

from contextlib import ExitStack

def context_stack(contexts):
    stack = ExitStack()
    for context in contexts:
        stack.enter_context(context)
    return stack

def patch_mocks():
    mocks = [
        patch('db_config.ReadWrite', db_mocks.ReadWrite),
        patch('db_config.ReadWrite', db_mocks.ReadWrite)
    ]
    return context_stack(mocks)

它被这样使用(简化):

with patch_mocks():
    LoadFiles(file_list)

LoadFiles将遍历file_list中的每个文件,并尝试将内容插入数据库。底层方法使用db_config.ReadWrite连接到数据库,但是当然它们由db_mocks.ReadWrite修补。除了看起来很随机外,此操作一直很稳定,但会失败,因为尝试创建连接时尝试使用db_config.ReadWrite

因此,例如,可能有一百个文件,它将成功修补其中的大多数文件,但是在中途它会随机停止使用该修补程序,从而使测试失败。哪些条件/变量可能导致此补丁不被应用?可以应用的补丁数量是否有限制?应该以其他方式应用吗?

2 个答案:

答案 0 :(得分:1)

我的第一行调查将涉及来自the docs on .patch()的警告:

target应该是“ package.module.ClassName”形式的字符串。导入目标并将指定的对象替换为新对象,因此目标必须可从您从其调用patch()的环境中导入。 目标是在执行修饰功能时而不是在修饰时导入的。

以及有关Where to patch

的进一步说明

基本原理是,您修补查找对象的位置,该对象不一定与定义对象的位置相同。

我会尝试查找一个破损的情况,并检查那里的导入环境的状态,以确保从那里可以访问您在其他任何地方使用的相同导入。

答案 1 :(得分:0)

请勿打补丁/模拟,而应使用repository pattern访问数据库。

然后您将具有Repository接口的两种实现:

  • 在内存中:将所有数据保留在内存中
  • 使用数据库驱动程序/连接器:实际写入数据库