当你尝试运行Pytest代码时,那些不相关的警告突然弹出,是不是感觉很烦人?



比如来自某个你无法控制的外部库的 DeprecationWarning(弃用警告)。



虽然警告有用,但它们会让你的控制台输出变得杂乱,掩盖重要的结果。



在 Pytest 中要如何消除这些警告呢?是不是该禁用所有警告?那重要的警告又该怎么办呢?



警告是一种重要信号,表明你代码中的某些内容可能不太对,但不一定是错误,比如已弃用的函数或潜在的运行时问题。



它们能帮你在问题升级为更严重的状况之前,预先察觉并改正。



在本文中,你将学习在 Pytest 中通过不同技巧优雅地处理警告。



首先,你会探索 warnings 模块,了解它的含义以及不同类型的警告。



接着,你将学习 Pytest 默认是如何管理警告的,以及如何覆盖配置以满足你的需求,同时还会了解一些管理警告的最佳实践。

理解 Python 警告



在深入代码之前,我们先来了解一下 Python 中的 warnings 模块是什么,以及它最初设计的目的。



warnings 模块是 BaseException 类的扩展,用于突出那些值得关注,但不像异常那样会导致程序中断的问题。



它可以通知你有关语法或功能的情况,这些语法或功能虽然仍能正常工作,但在未来的版本中可能不再受支持。



官方文档解释了不同类型的警告:



  • DeprecationWarning:警告关于那些预期在未来版本中会被移除的过时特性。
  • UserWarning:用户代码生成的警告的基类。
  • SyntaxWarning:警告可疑的语法。
  • RuntimeWarning:警告可疑的运行时行为。
  • FutureWarning:与 DeprecationWarning 类似,警告关于那些计划在未来某个时候停止使用的已弃用特性。


你可以使用 warnings 库轻松生成警告,例如:



warnings.warn(message, category=None, stacklevel=1)



  • message:警告消息,通常是一个字符串。
  • category:警告类别(例如 DeprecationWarningUserWarning),有助于像前面讨论的那样进行过滤。
  • stacklevel:一个整数,表示报告警告来源时使用的栈帧深度,帮助你确定警告是从哪里触发的。stacklevel 为 1 指向 warn() 函数的调用者。


在使用外部库时,你经常会遇到 DeprecationWarning 或 SyntaxWarning



既然你已经对警告有了一些了解,那我们接着来看你来这里的初衷 —— 如何在 Pytest 中忽略警告。



毕竟,外部库引发的警告不是你的问题,可以将其屏蔽。

在 Pytest 中完全禁用警告



你可以使用 --disable-warnings 命令行选项,从测试运行输出中完全抑制警告摘要。



$ pytest --disable-warnings
这是一个比较极端的选项,有其缺点,最大的问题是当出现与你相关且应该处理的警告时,你不会知晓(因为 Pytest 会过滤掉所有警告)。



你还可以使用 -p no:warnings 命令行选项禁用警告捕获。



$ pytest -p no:warnings
或者使用配置文件和 Pytest 的 addoption:



[pytest]  addopts = -p no:warnings
更好的选择是使用 pytest.ini 文件来抑制或过滤特定的警告。

抑制或过滤警告



Pytest 允许你使用 pytest.ini 文件和 filterwarnings 选项轻松地抑制或过滤警告。



这是一个好得多的选项,它允许你过滤掉特定的警告,而不是所有警告。

[pytest]  filterwarnings =          ignore::DeprecationWarning

这个设置将忽略测试模块中的任何 DeprecationWarning



下面的配置将忽略所有用户警告以及匹配正则表达式的特定弃用警告,但会将所有其他警告转换为错误。

[pytest]  filterwarnings =        error        ignore::UserWarning        ignore:function ham\(\) is deprecated:DeprecationWarning

抑制或过滤(特定)库的警告



一种更简洁的方法是忽略引发警告的特定库的警告,可以这样做:



[pytest]  filterwarnings =      ignore::DeprecationWarning:module_name.*
例如:



[pytest]  filterwarnings =      ignore::DeprecationWarning:botocore.*:      ignore::FutureWarning:pandas.*
这将抑制来自该库的所有警告。

示例代码组织结构如下



实例详细演示在Pytest中如何忽略警告_自动化测试



examples/my_module.py



import warnings    
def old_function():      warnings.warn(                "old_function() is deprecated; use new_function() instead.",                DeprecationWarning,                          stacklevel=2,                      )    
def new_function():      print("This is the new function.")      
old_function()

运行这段代码,我们会得到警告。



$ python examples/my_module.py

实例详细演示在Pytest中如何忽略警告_Python_02

让我们编写一个测试。



tests/test_warnings.py



import warnings  from examples.my_module import old_function    
def test_function():      old_function()

运行测试:



$ pytest

实例详细演示在Pytest中如何忽略警告_命令行_03

示例测试 —— 抑制警告



让我们包含一个 pytest.ini 文件,内容如下:



[pytest]  filterwarnings =      ignore::DeprecationWarning
运行这个测试:

实例详细演示在Pytest中如何忽略警告_pytest_04



很顺利。



让我们在代码中添加一个 SyntaxWarning



examples/my_module.py



def old_function():      warnings.warn(                    "old_function() is deprecated; use new_function() instead.",                    DeprecationWarning,                    stacklevel=2,                )                      warnings.warn(                    "The syntax will be removed in version 2.0.0.",                    SyntaxWarning,                    stacklevel=2,                )
让我们运行 Pytest:

实例详细演示在Pytest中如何忽略警告_pytest_05



如你所见,Pytest 抑制了 DeprecationWarning,但仍然显示了 SyntaxWarning



修改我们的 pytest.ini 文件:



[pytest]  filterwarnings =      ignore::DeprecationWarning            ignore::SyntaxWarning
现在我们得到了一个干净的输出。

实例详细演示在Pytest中如何忽略警告_pytest_06

你可以对警告及其抑制进行更多尝试。

使用标记过滤警告



Pytest 还提供了使用标记向特定测试和测试模块添加警告过滤器的选项,这样能实现更精细的控制。



import warnings  import pytest    def api_v1():      # 生成一个带有特定消息的警告。            warnings.warn("api v1, should use functions from v2", UserWarning)                  return 1          @pytest.mark.filterwarnings("ignore:api v1")  def test_one():      # 将忽略消息中包含 "api v1" 的警告。            assert api_v1() == 1

确保代码触发弃用警告



现在,如果你处于代码生成端,即你想测试你的代码是否触发 DeprecationWarning,也可以使用 Pytest 进行测试。



你可以使用 pytest.deprecated_call() 方法。



def test_myfunction_deprecated():      with pytest.deprecated_call():           old_function()
如果 old_function 没有发出弃用警告,这个测试将会失败。

实例详细演示在Pytest中如何忽略警告_自动化测试_07

结论



我们来到了文章末尾。



希望你找到了一直在寻找的内容 —— 如何在 Pytest 中轻松过滤警告,无论是来自你自己的库、外部包,甚至是 Pytest 本身的警告。



在本文中,我们介绍了一些关于 Python 警告的基础知识,它们是什么以及如何使用。



然后我们看了最重要的部分,即在 Pytest 中如何禁用或抑制警告(特别是来自外部库的警告)。



最后,你还学习了如何抑制特定库的警告,使用 Pytest 标记过滤警告,甚至断言你自己的 DeprecationWarning