Testing#

Matplotlib uses thepytest framework.

The tests are inlib/matplotlib/tests, and customizations to the pytesttesting infrastructure are inmatplotlib.testing.

Requirements#

To run the tests you will need toset up Matplotlib for development. Note inparticular theadditional dependencies for testing.

Note

We will assume that you want to run the tests in a development setup.

While you can run the tests against a regular installed version ofMatplotlib, this is a far less common use case. You still need theadditional dependencies for testing.You have to additionally get the reference images from the repository,because they are not distributed with pre-built Matplotlib packages.

Running the tests#

In the root directory of your development repository run:

pytest

pytest can be configured via manycommand-line parameters. Some particularly useful ones are:

-v or--verbose

Be more verbose

-nNUM

Run tests in parallel over NUMprocesses (requirespytest-xdist)

--capture=no or-s

Do not capture stdout

To run a single test from the command line, you can provide a file path, optionallyfollowed by the function separated by two colons, e.g., (tests do not need to beinstalled, but Matplotlib should be):

pytestlib/matplotlib/tests/test_simplification.py::test_clipping

If you want to usepytest as a module (viapython-mpytest), then you will needto avoid clashes betweenpytest's import mode and Python's search path:

  • On more recent Python, you maydisable"unsafeimportpaths" (i.e., stop adding the current directory to the import path) with the-P argument:

    python-P-mpytest
  • On older Python, you may enableisolatedmode(which stops adding the current directory to the import path, but has otherrepercussions):

    python-I-mpytest
  • On any Python, setpytest'simport mode to the olderprepend mode (but note that this will breakpytest's assert rewriting):

    python-mpytest--import-modeprepend

Viewing image test output#

The output ofimage-based tests is stored in aresult_images directory. These images can be compiled into one HTML page, containinghundreds of images, using thevisualize_tests tool:

pythontools/visualize_tests.py

Image test failures can also be analysed using thetriage_tests tool:

pythontools/triage_tests.py

The triage tool allows you to accept or reject test failures and will copy the new imageto the folder where the baseline test images are stored. The triage tool requires thatQT is installed.

Writing a simple test#

Many elements of Matplotlib can be tested using standard tests. Forexample, here is a test frommatplotlib/tests/test_basic.py:

deftest_simple():"""    very simple example test    """assert1+1==2

Pytest determines which functions are tests by searching for files whose namesbegin with"test_" and then within those files for functions beginning with"test" or classes beginning with"Test".

Some tests have internal side effects that need to be cleaned up after theirexecution (such as created figures or modifiedrcParams). The pytest fixturematplotlib.testing.conftest.mpl_test_settings will automatically cleanthese up; there is no need to do anything further.

Random data in tests#

Random data is a very convenient way to generate data for examples,however the randomness is problematic for testing (as the testsmust be deterministic!). To work around this set the seed in each test.For numpy's default random number generator use:

importnumpyasnprng=np.random.default_rng(19680801)

and then userng when generating the random numbers.

The seed isJohn Hunter's birthday.

Writing an image comparison test#

Writing an image-based test is only slightly more difficult than a simpletest. The main consideration is that you must specify the "baseline", orexpected, images in theimage_comparisondecorator. For example, this test generates a single image and automaticallytests it:

frommatplotlib.testing.decoratorsimportimage_comparisonimportmatplotlib.pyplotasplt@image_comparison(baseline_images=['line_dashes'],remove_text=True,extensions=['png'],style='mpl20')deftest_line_dashes():fig,ax=plt.subplots()ax.plot(range(10),linestyle=(0,(3,3)),lw=5)

The first time this test is run, there will be no baseline image to compareagainst, so the test will fail. Copy the output images (in this caseresult_images/test_lines/test_line_dashes.png) to the correctsubdirectory ofbaseline_images tree in the source directory (in thiscaselib/matplotlib/tests/baseline_images/test_lines). Put this newfile under source code revision control (withgitadd). When rerunningthe tests, they should now pass.

It is preferred that new tests usestyle='mpl20' as this leads to smallerfigures and reflects the newer look of default Matplotlib plots. Also, if thetexts (labels, tick labels, etc) are not really part of what is tested, use theremove_text=True argument or add thetext_placeholders fixture as thiswill lead to smaller figures and reduce possible issues with font mismatch ondifferent platforms.

Compare two methods of creating an image#

Baseline images take a lot of space in the Matplotlib repository.An alternative approach for image comparison tests is to use thecheck_figures_equal decorator, which should beused to decorate a function taking twoFigure parameters and draws the sameimages on the figures using two different methods (the tested method and thebaseline method). The decorator will arrange for setting up the figures andthen collect the drawn results and compare them.

For example, this test compares two different methods to draw the samecircle: plotting a circle using amatplotlib.patches.Circle patchvs plotting the circle using the parametric equation of a circle

frommatplotlib.testing.decoratorsimportcheck_figures_equalimportmatplotlib.patchesasmpatchesimportmatplotlib.pyplotaspltimportnumpyasnp@check_figures_equal()deftest_parametric_circle_plot(fig_test,fig_ref):xo=yo=0.5radius=0.4ax_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)foraxin[ax_ref,ax_test]:ax.set(xlim=(0,1),ylim=(0,1),aspect='equal')

Both comparison decorators have a tolerance argumenttol that is used to specify thetolerance for difference in color value between the two images, where 255 is the maximaldifference. The test fails if the average pixel difference is greater than this value.

See the documentation ofimage_comparison andcheck_figures_equal for additional informationabout their use.

Creating a new module in matplotlib.tests#

We try to keep the tests categorized by the primary module they aretesting. For example, the tests related to themathtext.py moduleare intest_mathtext.py.

Using GitHub Actions for CI#

GitHub Actions is a hosted CI system"in the cloud".

GitHub Actions is configured to receive notifications of new commits to GitHubrepos and to run builds or tests when it sees these new commits. It looks for aYAML files in.github/workflows to see how to test the project.

GitHub Actions is already enabled for themain Matplotlib GitHub repository -- for example, seethe Testsworkflows.

GitHub Actions should be automatically enabled for your personal Matplotlibfork once the YAML workflow files are in it. It generally isn't necessary tolook at these workflows, since any pull request submitted against the mainMatplotlib repository will be tested. The Tests workflow is skipped in forkedrepositories but you can trigger a run manually from theGitHub web interface.

You can see the GitHub Actions results atyour_GitHub_user_name/matplotlib -- here'sanexample.

Using tox#

Tox is a tool for running testsagainst multiple Python environments, including multiple versions of Python(e.g., 3.10, 3.11) and even different Python implementations altogether(e.g., CPython, PyPy, Jython, etc.), as long as all these versions areavailable on your system's $PATH (consider using your system package manager,e.g. apt-get, yum, or Homebrew, to install them).

tox makes it easy to determine if your working copy introduced anyregressions before submitting a pull request. Here's how to use it:

$pipinstalltox$tox

You can also run tox on a subset of environments:

$tox-epy310,py311

Tox processes everything serially so it can take a long time to testseveral environments. To speed it up, you might try using a new,parallelized version of tox calleddetox. Give this a try:

$pipinstall-U-ihttp://pypi.testrun.orgdetox$detox

Tox is configured using a file calledtox.ini. You may need toedit this file if you want to add new environments to test (e.g.,py33) or if you want to tweak the dependencies or the way thetests are run. For more info on thetox.ini file, see theToxConfiguration Specification.

Building old versions of Matplotlib#

When running agitbisect to see which commit introduced a certain bug,you may (rarely) need to build very old versions of Matplotlib. The followingconstraints need to be taken into account:

  • Matplotlib 1.3 (or earlier) requires numpy 1.8 (or earlier).

Testing released versions of Matplotlib#

Running the tests on an installation of a released version (e.g. PyPI packageor conda package) also requires additional setup.

Note

For an end-user, there is usually no need to run the tests on releasedversions of Matplotlib. Official releases are tested before publishing.

Install additional dependencies#

Install theadditional dependencies for testing.

Obtain the reference images#

Many tests compare the plot result against reference images. The referenceimages are not part of the regular packaged versions (pip wheels or condapackages). If you want to run tests with reference images, you need to obtainthe reference images matching the version of Matplotlib you want to test.

To do so, either download the matching source distributionmatplotlib-X.Y.Z.tar.gz fromPyPIor alternatively, clone the git repository andgitcheckoutvX.Y.Z. Copythe folderlib/matplotlib/tests/baseline_images to the foldermatplotlib/tests of your the matplotlib installation to test.The correct target folder can be found using:

python-c"import matplotlib.tests; print(matplotlib.tests.__file__.rsplit('/', 1)[0])"

An analogous copying oflib/mpl_toolkits/*/tests/baseline_imagesis necessary for testingmpl_toolkits.

Run the tests#

To run all the tests on your installed version of Matplotlib:

pytest--pyargsmatplotlib.tests

The test discovery scope can be narrowed to single test modules or even singlefunctions:

pytest--pyargsmatplotlib.tests.test_simplification.py::test_clipping