【问题标题】:Mocking a module import in pytest在 pytest 中模拟模块导入
【发布时间】:2020-02-02 05:24:27
【问题描述】:

我正在编写一个 pytest 插件,它应该测试旨在在一组特定环境中工作的软件。

我正在编写的软件在一个更大的框架中运行,这使得某些 Python 模块只有在框架中运行我的 Python 软件时才可用。

为了测试我的软件,我需要“模拟”或伪造整个模块(实际上,相当多)。我需要以某种类似的方式实现它的功能,但我的问题是我应该如何使用 py.test 插件使这个假 Python 模块可用于我的软件代码?

例如,假设我的一个源文件中有以下代码:

import fwlib

def fw_sum(a, b):
    return fwlib.sum(a, b)

但是,fwlib 模块只能由我运行我的软件的框架提供,我无法在其中进行测试。

如何确保在 pytest 插件中名为 fwlib 的模块已在 sys.modules 中定义?当然,我需要自己实现fwlib.sum。我正在寻找有关如何做到这一点的建议。

【问题讨论】:

    标签: python pytest


    【解决方案1】:

    pytest 为这个用例提供了一个夹具:monkeypatch.syspath_prepend

    您可以在sys.path 导入位置列表前面添加路径。编写一个假的fwlib.py 并将其包含在您的测试中,并根据需要附加目录。与其他测试模块一样,它不需要包含在发行版中。

    在我自己玩过这个之后,我实际上无法弄清楚如何让夹具从库代码中正确模拟模块级导入。到测试运行时,库代码已经导入,再打补丁就来不及了。

    但是,我可以提供另一种可行的解决方案:您可以从conftest.pywhich gets imported first 中注入名称。被测代码中的后续 import 语句只会重用 sys.modules 中已经存在的对象。

    包结构:

    $ tree .
    .
    ├── conftest.py
    ├── lib
    │   └── my_lib.py
    └── tests
        └── test_my_lib.py
    
    2 directories, 3 files
    

    文件内容:

    # conftest.py
    import sys
    
    def fwlib_sum(a, b):
        return a + b
    
    module = type(sys)('fwlib')
    module.sum = fwlib_sum
    sys.modules['fwlib'] = module
    

    库文件:

    # lib/my_lib.py
    import fwlib
    
    def fw_sum(a, b):
        return fwlib.sum(a, b)
    

    测试文件:

    # lib/test_my_lib.py
    import my_lib
    
    def test_sum():
        assert my_lib.fw_sum(1, 2) == 3
    

    【讨论】:

    • 这太好了,谢谢!您能否澄清最后一条声明,关于它不需要包含在发行版中吗?
    • 大概你有一个./tests/ 子目录,在创建发行版时没有被你的setup.py 文件打包。例如,您可以创建可以在测试运行期间导入的 ./tests/mocks/fwlib.py,但它不会与您的库一起分发。
    • 啊,我明白了。我会稍等片刻,等待其他答案的出现(尽管不太可能)并尽快接受您的回答。再次感谢!
    • 当导入类似于“from import ”时,有谁知道该怎么做?
    • @Neosapien 同样的方法仍然有效。注入sys.modules 名称。
    猜你喜欢
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 2019-11-07
    • 1970-01-01
    • 2018-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多