测试#
Matplotlib 使用 pytest 框架.
这些测试位于 lib/matplotlib/tests 中,而对 pytest 测试基础结构的自定义位于 matplotlib.testing 中.
需求#
要运行测试,您将需要 set up Matplotlib for development .请特别注意测试的 additional dependencies .
备注
我们将假定您想在开发设置中运行测试.
虽然您可以针对常规安装的 Matplotlib 版本运行测试,但这不是一个常见的用例.您仍然需要测试的 additional dependencies .您还必须从存储库中获取参考图像,因为它们不随预构建的 Matplotlib 软件包一起分发.
运行测试#
在开发存储库的根目录中运行:
pytest
可以通过许多 :external+pytest command-line parameters 配置 pytest .一些特别有用的参数是:
|
更详细 |
|
通过 NUM 进程并行运行测试(需要 pytest-xdist) |
|
不捕获 stdout |
要从命令行运行单个测试,您可以提供文件路径,可以选择后跟用两个冒号分隔的函数,例如(测试不需要安装,但应该安装Matplotlib):
pytest lib/matplotlib/tests/test_simplification.py::test_clipping
如果要将 pytest 用作模块(通过 python -m pytest ),则需要避免 pytest 的导入模式和Python的搜索路径之间的冲突:
在较新版本的 Python 中,您可以::external+python
disable "unsafe import paths"(即,停止将当前目录添加到导入路径)使用-P参数:python -P -m pytest
在较旧版本的 Python 中,您可以启用 :external+python
isolated mode(它会停止将当前目录添加到导入路径,但会有其他影响):python -I -m pytest
在任何 Python 版本中,将
pytest的 :external+pytest import mode 设置为较旧的prepend模式(但请注意,这会破坏pytest的断言重写):python -m pytest --import-mode prepend
查看图像测试输出#
image-based 测试的输出存储在 result_images 目录中.可以使用 visualize_tests 工具将这些图像编译成一个包含数百张图像的 HTML 页面:
python tools/visualize_tests.py
图像测试失败也可以使用 triage_tests 工具进行分析:
python tools/triage_tests.py
使用 triage 工具可以接受或拒绝测试失败,并将新图像复制到存储基线测试图像的文件夹中.triage 工具需要安装 QT .
编写一个简单的测试#
可以使用标准测试来测试 Matplotlib 的许多元素.例如,以下是 matplotlib/tests/test_basic.py 中的一个测试:
def test_simple():
"""
very simple example test
"""
assert 1 + 1 == 2
Pytest 通过搜索名称以 "test_" 开头的文件,然后在这些文件中搜索以 "test" 开头的函数或以 "Test" 开头的类来确定哪些函数是测试.
某些测试具有内部副作用,需要在执行后进行清理(例如创建的图形或修改的 .rcParams ).pytest fixture matplotlib.testing.conftest.mpl_test_settings 将自动清理这些副作用;无需执行任何进一步操作.
测试中的随机数据#
随机数据是为示例生成数据的一种非常便捷的方法,但是随机性对于测试来说是有问题的(因为测试必须是确定性的!).要解决此问题,请在每个测试中设置种子.对于 numpy 的默认随机数生成器,请使用:
import numpy as np
rng = np.random.default_rng(19680801)
然后在使用随机数时使用 rng .
该种子是 John Hunter's 生日.
编写图像比较测试#
编写基于图像的测试仅比编写简单测试稍难.主要考虑因素是,您必须在 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 已经为 main Matplotlib GitHub repository 启用 -- 例如,参见 the Tests workflows .
一旦 YAML 工作流程文件存在于你的个人 Matplotlib fork 中,GitHub Actions 应该会自动为你启用.通常没有必要查看这些工作流程,因为针对主 Matplotlib 仓库提交的任何 pull request 都会被测试.Tests 工作流程在 fork 的仓库中会被跳过,但是你可以从 GitHub web interface 手动触发运行.
你可以在 your_GitHub_user_name/matplotlib 查看 GitHub Actions 的结果 -- 这里有一个 an example .
使用 tox#
Tox 是一个针对多个 Python 环境运行测试的工具,包括多个 Python 版本(例如,3.10,3.11),甚至完全不同的 Python 实现(例如,CPython,PyPy,Jython 等),只要所有这些版本在你的系统的 $PATH 中可用(考虑使用你的系统包管理器,例如 apt-get,yum 或 Homebrew 来安装它们).
tox 使你很容易确定你的工作副本是否引入了任何回归,然后再提交 pull request.以下是如何使用它:
$ 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 Configuration Specification .
构建旧版本的 Matplotlib#
当运行 git bisect 来查看哪个提交引入了某个 bug 时,你可能(很少)需要构建非常旧版本的 Matplotlib.以下约束需要考虑:
Matplotlib 1.3(或更早版本)需要 numpy 1.8(或更早版本).
测试已发布的 Matplotlib 版本#
在已发布版本(例如,PyPI 包或 conda 包)的安装上运行测试也需要额外的设置.
备注
对于最终用户而言,通常不需要在已发布的 Matplotlib 版本上运行测试.官方版本在发布前会经过测试.
安装额外的依赖项#
获取参考图像#
许多测试会将绘图结果与参考图像进行比较.参考图像不是常规打包版本(pip wheels 或 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