【问题标题】:How to remove a library with monkeypatch or mock in pytest?如何在pytest中删除带有monkeypatch或mock的库?
【发布时间】:2020-12-31 01:44:20
【问题描述】:

如果我的库有一个 contrib 附加组件,其中包含依赖项(例如 requests),我希望用户必须安装它才能访问 CLI API,但我在 CI 测试期间安装了附加组件如何在测试期间使用pytest's MonkeyPatch 去除依赖关系以确保我的检测正确?

例如,如果contrib 额外安装requests,那么我希望用户必须这样做

$ python -m pip install mylib[contrib]

然后能够在命令行拥有一个看起来像的 CLI API

$ mylib contrib myfunction

其中myfunction 使用requests 依赖项

# mylib/src/mylib/cli/contrib.py
import click
try:
    import requests
except ModuleNotFoundError:
    pass # should probably warn though, but this is just an example

# ...

@click.group(name="contrib")
def cli():
    """
    Contrib experimental operations.
    """

@cli.command()
@click.argument("example", default="-")
def myfunction(example):
   requests.get(example)
   # ...

我如何在我的pytest 测试中模拟或修改requests out,这样我就可以确保用户会正确地收到警告以及ModuleNotFoundError 如果他们只是这样做

$ python -m pip install mylib
$ mylib contrib myfunction

?在阅读了关于 pytest 标签的其他一些问题后,我仍然认为我不明白如何做到这一点,所以我在这里问。

【问题讨论】:

标签: python pytest monkeypatching pytest-mock


【解决方案1】:

我最终做了什么,我已经确认这是一个合理的方法thanks to Anthony Sottile,是通过在@987654328 中将它设置为None 来模拟额外的依赖项(这里是requests)不存在@ 然后重新加载需要使用requests 的模块。 我测试有一个实际的投诉,即requests 不存在使用caplog 导入。

这是我目前正在使用的测试(名称已更改以匹配上面问题中的玩具示例问题)

import mylib
import sys
import logging
import pytest
from unittest import mock
from importlib import reload
from importlib import import_module

# ...

def test_missing_contrib_extra(caplog):
    with mock.patch.dict(sys.modules):
        sys.modules["requests"] = None
        if "mylib.contrib.utils" in sys.modules:
            reload(sys.modules["mylib.contrib.utils"])
        else:
            import_module("mylib.cli")

    with caplog.at_level(logging.ERROR):
        # The 2nd and 3rd lines check for an error message that mylib throws
        for line in [
            "import of requests halted; None in sys.modules",
            "Installation of the contrib extra is required to use mylib.contrib.utils.download",
            "Please install with: python -m pip install mylib[contrib]",
        ]:
            assert line in caplog.text
        caplog.clear()

我应该注意到这实际上是在@hoefling 链接到上面的@Abhyudai's answer to "Test for import of optional dependencies in init.py with pytest: Python 3.5 /3.6 differs in behaviour" 中提倡的(在我解决了这个问题之后但在我开始发布之前发布)。

如果人们有兴趣在实际图书馆中看到这一点,请参阅以下两个 PR:

注意事项: Anthony Sottile 警告说

reload() 可能有点不确定——我会小心处理它(对旧模块有旧引用的东西会继续存在,有时它会引入新的单件副本(双件件?三件件?))——我已将多次测试污染问题追踪到reload()

因此,如果我实施更安全的替代方案,我将修改此答案。

【讨论】:

  • 也许是为了澄清 - 但monkeypatch.setitem 可能更好依赖?
  • 从这个 Twitter 帖子中,Anthony 会说不:twitter.com/codewithanthony/status/1338547566607101952“我个人的意见是永远不要使用 monkeypatch 夹具,因为模拟范围没有得到很好的定义/控制”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-23
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
  • 2020-12-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多