将 `runpy` 与 mocks 组合在单独 运行 时通过,但在 运行 其他测试时失败

Combining `runpy` with mocks passes when running alone but fails when running with other tests

当我运行单独进行以下测试时,它通过了。但是当我 运行 它与其他测试一起使用时,即使这个测试是第一个 运行 它失败了:


from runpy import run_path
from unittest.mock import patch, MagicMock

@patch('boto3.client', autospec=True)
def test_scripts(mock_boto3_client: MagicMock):
    mock_boto3_client().head_object.return_value = {}
    run_path(
      'src/scripts/dynamic_script.py',
      run_name='__main__'
    )

这似乎会以某种方式受到其他模拟或导入的影响,但尚不清楚当 运行 首先

时它们将如何影响此测试

终于想通了。这是由于 mock.patch 的工作方式。这是一个很好的解释:

https://nedbatchelder.com/blog/201908/why_your_mock_doesnt_work.html

runpy 之所以难以理解,是因为当我们 运行 仅进行此测试时,即使补丁名称不正确,补丁也能正常工作。这可能是因为补丁是在导入 之前 完成的(导入在 run_path 期间动态发生),所以在 from/import 发生时,补丁已经存在在内存中,我们得到了指向模拟的指针。

当我们 运行 在其他测试的上下文中进行测试时,导入树包含使用 from boto3 import client 定义的原始库,因此此导入可能在应用补丁之前在内存中。

解决方案是确保所有对模拟实体的引用都是全名的,只使用 import boto3,或者在测试模块中模拟名称。