测试#
Matplotlib 使用 pytest 框架。
测试位于 lib/matplotlib/tests
,而针对 pytest 测试基础设施的自定义设置在 matplotlib.testing
中。
要求#
要运行测试,您需要 为开发设置 Matplotlib。特别注意 测试的额外依赖。
备注
我们将假设您希望在一个开发环境中运行测试。
虽然你可以针对常规安装的 Matplotlib 版本运行测试,但这是一种远不常见的使用情况。你仍然需要 额外的依赖项 来进行测试。你还需要从仓库中获取参考图像,因为它们不随预构建的 Matplotlib 包分发。
运行测试#
在你的开发仓库的根目录下运行:
pytest
pytest
可以通过许多 命令行参数 进行配置。其中一些特别有用的参数包括:
|
更详细 |
|
在 NUM 个进程上并行运行测试(需要 pytest-xdist) |
|
不要捕获标准输出 |
要从命令行运行单个测试,您可以提供文件路径,后面可以选择性地加上由两个冒号分隔的函数,例如,(测试不需要安装,但应安装 Matplotlib):
pytest lib/matplotlib/tests/test_simplification.py::test_clipping
如果你想通过 python -m pytest
将 pytest
作为一个模块使用,那么你需要避免 pytest
的导入模式与 Python 的搜索路径之间的冲突:
在较新的 Python 版本中,您可以使用
-P
参数 :external+python:std:option:`禁用“不安全的导入路径” <-P>`(即,停止将当前目录添加到导入路径中):python -P -m pytest
在较旧的 Python 版本中,您可以启用
隔离模式
(这将阻止将当前目录添加到导入路径中,但会产生其他影响):python -I -m pytest
在任何 Python 环境中,将
pytest
的 导入模式 设置为旧的prepend
模式(但请注意,这将破坏pytest
的断言重写):python -m pytest --import-mode prepend
查看图像测试输出#
基于 图像 测试的输出存储在 result_images
目录中。这些图像可以使用 visualize_tests
工具编译成一个包含数百张图像的HTML页面:
python tools/visualize_tests.py
图像测试失败也可以使用 triage_tests
工具进行分析:
python tools/triage_tests.py
分类工具允许你接受或拒绝测试失败,并将新图像复制到存储基线测试图像的文件夹中。分类工具要求 QT 已安装。
编写一个简单的测试#
Matplotlib 的许多元素可以使用标准测试进行测试。例如,以下是来自 matplotlib/tests/test_basic.py
的测试:
def test_simple():
"""
very simple example test
"""
assert 1 + 1 == 2
Pytest 通过搜索文件名以 "test_"
开头的文件,然后在这些文件中查找以 "test"
开头的函数或以 "Test"
开头的类来确定哪些函数是测试。
一些测试有内部副作用,需要在执行后进行清理(例如创建的图形或修改的 rcParams
)。pytest 夹具 matplotlib.testing.conftest.mpl_test_settings
将自动清理这些副作用;无需进一步操作。
测试中的随机数据#
随机数据是生成示例数据的非常方便的方法,然而随机性在测试中是有问题的(因为测试必须是确定性的!)。为了解决这个问题,在每个测试中设置种子。对于numpy的默认随机数生成器,使用:
import numpy as np
rng = np.random.default_rng(19680801)
然后在生成随机数时使用 rng
。
种子是 John Hunter 的 生日。
编写图像比较测试#
编写基于图像的测试仅比简单测试稍难一些。主要考虑的是,您必须在 image_comparison
装饰器中指定“基线”或预期的图像。例如,此测试生成单个图像并自动测试它:
from matplotlib.testing.decorators import image_comparison
import matplotlib.pyplot as plt
@image_comparison(baseline_images=['line_dashes'], remove_text=True,
extensions=['png'], style='mpl20')
def test_line_dashes():
fig, ax = plt.subplots()
ax.plot(range(10), linestyle=(0, (3, 3)), lw=5)
第一次运行此测试时,将没有基准图像进行比较,因此测试将失败。将输出图像(在本例中为 result_images/test_lines/test_line_dashes.png
)复制到源目录中 baseline_images
树的正确子目录(在本例中为 lib/matplotlib/tests/baseline_images/test_lines
)。将此新文件置于源代码修订控制下(使用 git add
)。重新运行测试时,它们现在应该通过。
建议新测试使用 style='mpl20'
,因为这会生成较小的图形并反映 Matplotlib 默认绘图的较新外观。此外,如果文本(标签、刻度标签等)不是测试的真正部分,请使用 remove_text=True
,因为这将生成较小的图形并减少不同平台上字体不匹配的可能问题。
比较两种创建图像的方法#
基准图像在 Matplotlib 仓库中占用大量空间。图像比较测试的另一种方法是使用 check_figures_equal
装饰器,该装饰器应用于一个函数,该函数接受两个 Figure
参数,并使用两种不同的方法(被测试的方法和基准方法)在图上绘制相同的图像。装饰器将负责设置图形,然后收集绘制的结果并进行比较。
例如,此测试比较了两种不同的方法来绘制相同的圆:使用 matplotlib.patches.Circle
补丁绘制圆与使用圆的参数方程绘制圆
from matplotlib.testing.decorators import check_figures_equal
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np
@check_figures_equal()
def test_parametric_circle_plot(fig_test, fig_ref):
xo, yo= (.5, .5)
radius = 0.4
ax_test = fig_test.subplots()
theta = np.linspace(0, 2 * np.pi, 150)
l, = ax_test.plot(xo + (radius * np.cos(theta)),
yo + (radius * np.sin(theta)), c='r')
ax_ref = fig_ref.subplots()
red_circle_ref = mpatches.Circle((xo, yo), radius, ec='r', fc='none',
lw=l.get_linewidth())
ax_ref.add_artist(red_circle_ref)
for ax in [ax_ref, ax_test]:
ax.set(xlim=(0,1), ylim=(0,1), aspect='equal')
两个比较装饰器都有一个容差参数 tol
,用于指定两幅图像之间颜色值差异的容差,其中255是最大差异。如果平均像素差异大于此值,则测试失败。
有关其使用的更多信息,请参阅 image_comparison
和 check_figures_equal
的文档。
在 matplotlib.tests 中创建一个新模块#
我们尝试将测试按其主要测试的模块进行分类。例如,与 mathtext.py
模块相关的测试位于 test_mathtext.py
中。
使用 GitHub Actions 进行 CI#
GitHub Actions 是一个托管在云端的CI系统。
GitHub Actions 配置为接收 GitHub 仓库新提交的通知,并在检测到这些新提交时运行构建或测试。它会在 .github/workflows
中查找 YAML 文件以了解如何测试项目。
GitHub Actions 已经为 主 Matplotlib GitHub 仓库 启用 -- 例如,请参见 测试工作流程。
GitHub Actions 应该会在 YAML 工作流文件放入你的个人 Matplotlib 分支后自动启用。通常不需要查看这些工作流,因为任何提交到主 Matplotlib 仓库的拉取请求都会被测试。测试工作流在分支仓库中会被跳过,但你可以从 GitHub 网页界面 手动触发运行。
你可以在 your_GitHub_user_name/matplotlib 查看 GitHub Actions 的结果 -- 这里有一个 例子。
使用 tox#
Tox 是一个用于在多个 Python 环境中运行测试的工具,包括多个版本的 Python(例如,3.10、3.11)甚至不同的 Python 实现(例如,CPython、PyPy、Jython 等),只要这些版本在系统的 $PATH 中可用(考虑使用系统的包管理器,例如 apt-get、yum 或 Homebrew 来安装它们)。
tox 使得在提交拉取请求之前,轻松确定你的工作副本是否引入了任何回归问题。以下是使用方法:
$ pip install tox
$ tox
你也可以在部分环境中运行 tox:
$ tox -e py310,py311
Tox 按顺序处理所有内容,因此测试多个环境可能需要很长时间。为了加快速度,您可以尝试使用一个名为 detox
的新并行化版本的 tox。试试这个:
$ pip install -U -i http://pypi.testrun.org detox
$ detox
Tox 使用一个名为 tox.ini
的文件进行配置。如果你想添加新的测试环境(例如 py33
)或调整依赖项或测试运行方式,你可能需要编辑此文件。有关 tox.ini
文件的更多信息,请参阅 Tox 配置规范。
构建旧版本的 Matplotlib#
当运行 git bisect
以查看哪个提交引入了某个错误时,您可能(很少)需要构建非常旧版本的 Matplotlib。需要考虑以下约束:
Matplotlib 1.3(或更早版本)需要 numpy 1.8(或更早版本)。
测试 Matplotlib 的发布版本#
在已发布的版本(例如 PyPI 包或 conda 包)上运行测试也需要额外的设置。
备注
对于终端用户来说,通常不需要在发布的 Matplotlib 版本上运行测试。官方发布版本在发布前已经过测试。
安装额外的依赖项#
安装 测试所需的额外依赖项。
获取参考图像#
许多测试将绘图结果与参考图像进行比较。参考图像不是常规打包版本(pip 轮或 conda 包)的一部分。如果你想使用参考图像运行测试,你需要获取与你想要测试的 Matplotlib 版本相匹配的参考图像。
为此,可以从 PyPI 下载匹配的源代码分发包 matplotlib-X.Y.Z.tar.gz
,或者克隆 git 仓库并执行 git checkout vX.Y.Z
。将文件夹 lib/matplotlib/tests/baseline_images
复制到你的 matplotlib 安装目录下的 matplotlib/tests
文件夹中进行测试。正确的目标文件夹可以通过以下方式找到:
python -c "import matplotlib.tests; print(matplotlib.tests.__file__.rsplit('/', 1)[0])"
对于测试 mpl_toolkits
,需要类似地复制 lib/mpl_toolkits/*/tests/baseline_images
。
运行测试#
要在您安装的 Matplotlib 版本上运行所有测试:
pytest --pyargs matplotlib.tests
测试发现范围可以缩小到单个测试模块,甚至单个函数:
pytest --pyargs matplotlib.tests.test_simplification.py::test_clipping