首页 > 解决方案 > 如果未安装可选模块,则测试代码行为

问题描述

我有一个软件包,packageA,它对packageB. 如果packageB已安装,则用户可以访问一些附加功能。但是,没有安装它并不会限制packageA.

我想编写一个单元(或集成?)测试,packageA如果没有安装,检查仍然可用packageB......也就是说,它不会意外地抛出一个ImportErrorforpackagB

我的测试是这样写的:

from unittest import mock
import pytest


def test_no_err_if_packageb_not_installed():
    import sys
    sys.modules['packageB'] = mock.MagicMock()
    try:
        from packageA import file_that_uses_packageb  # noqa: F401
    except NameError:
        pytest.fail("packagB dependency found at root level")

这有效,如果它被孤立地调用。但是当它作为测试套件的一部分被调用时,这个测试总是会通过。这是因为sys.modules之前的测试已经用导入模块填充了。我del从一开始就尝试过包装sys.modules,但这不起作用

编写此测试的正确方法是什么?

使用工作解决方案进行编辑

from unittest import mock
import pytest


def test_no_err_if_packageb_not_installed():
    import sys

    # clear cached modules
    mods = list(sys.modules.keys())
    for mod in mods:
        if 'packageA' in mod or 'packageB' in mod:
            del sys.modules[mod]

    sys.modules['packageB'] = mock.MagicMock()
    try:
        from packageA import file_that_uses_packageb  # noqa: F401
    except NameError:
        pytest.fail("packagB dependency found at root level")

    del sys.modules['packageB']

标签: pythonunit-testingpytest

解决方案


为此进行黑客攻击sys.modules是要走的路。问题是您正在packageB重置的导入 - 但实际上,要进行此测试,您还必须重置file_that_uses_packageb.

在您的测试设置中尝试del sys.modules["packageB"], sys.modules["packageA.file_that_uses_packageB"],您应该已经准备就绪。

不要忘记在测试结束时再次重置它们,否则实际使用的其他测试packageB将失败。

(被诱人标记为“重复”的答案没有触及相同的点 - 答案谈到 using imp.reload,它重置并重新运行一个模块,好的 - 但不允许你用模拟替换模块代码,如果需要)


推荐阅读