Repository navigation
The example of the testing decorator does not work. #18168
Copy link
Copy link
Closed
Labels
Milestone
Description
Bug report
Bug summary
The example of the testing decorator does not work.
https://matplotlib.org/3.2.1/devel/testing.html#writing-an-image-comparison-test
Code for reproduction
from matplotlib.testing.decorators import image_comparison
import matplotlib.pyplot as plt
@image_comparison(baseline_images=['line_dashes'], remove_text=True,
extensions=['png'])
def test_line_dashes():
fig, ax = plt.subplots()
ax.plot(range(10), linestyle=(0, (3, 3)), lw=5)pytest
Actual outcome
=================================== test session starts ===================================
platform linux -- Python 3.7.6, pytest-5.4.3, py-1.8.2, pluggy-0.13.1
rootdir: /home/smirnov/langlearn/mpltest
plugins: openfiles-0.5.0, astropy-header-0.1.2, arraydiff-0.3, hypothesis-5.16.1, remotedata-0.3.2, doctestplus-0.7.0
collected 0 items / 1 error
========================================= ERRORS ==========================================
______________________________ ERROR collecting test_mpl.py _______________________________
In test_line_dashes: function uses no argument 'extension'
==================================== warnings summary =====================================
/home/smirnov/anaconda3/lib/python3.7/site-packages/matplotlib/testing/decorators.py:242
/home/smirnov/anaconda3/lib/python3.7/site-packages/matplotlib/testing/decorators.py:242: PytestUnknownMarkWarning: Unknown pytest.mark.baseline_images - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/latest/mark.html
@pytest.mark.baseline_images(baseline_images)
/home/smirnov/anaconda3/lib/python3.7/site-packages/matplotlib/testing/decorators.py:244
/home/smirnov/anaconda3/lib/python3.7/site-packages/matplotlib/testing/decorators.py:244: PytestUnknownMarkWarning: Unknown pytest.mark.style - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/latest/mark.html
@pytest.mark.style(style)
-- Docs: https://docs.pytest.org/en/latest/warnings.html
================================= short test summary info =================================
ERROR test_mpl.py
!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!
============================== 2 warnings, 1 error in 0.39s ===============================
Expected outcome
Matplotlib version
- Operating system: Windows 10 (native and WSL2)
- Matplotlib version: 3.1.3
- Matplotlib backend (
print(matplotlib.get_backend())): not applicable - Python version: Python 3.7.7 (default, May 6 2020, 11:45:54) [MSC v.1916 64 bit (AMD64)]
- Jupyter version (if applicable): not applicable
- Other libraries: pytest 5.4.3
pip install <package that depends on matplotlib and has it in requirements> within the new Conda environment.
I want to use something to test whether the figures generated by my code look the same/similar comparing to the figures generated earlier.
Reactions are currently unavailable
Activity
QuLogic commentedon Aug 4, 2020
You need to update Matplotlib, or downgrade pytest.
SmirnGreg commentedon Aug 6, 2020
@QuLogic
Thanks, with mpl 3.3.0 works fine, with 3.2 does not work.
SmirnGreg commentedon Aug 6, 2020
I still have an issue with testing. Tests work fine when I run them one by one, but when I run
pyteston a whole folder, each following test remembers about previously opened figures, and tries to compare all of them with the saved baseline image.Should I implicitly close all the opened figures in the beginning of the test with
plt.close('all')? This works...Input:
Output of pytest on this file:
jklymak commentedon Aug 6, 2020
@SmirnGreg perhaps discuss this at discourse.matplotlib.org ?
SmirnGreg commentedon Aug 6, 2020
Dear @jklymak
I can discourse that on stack overflow or on some other forum as well, but I believe there is a severe issue in the image testing within matplotlib that might result into the library code change.
Let me explain my point. Matplotlib is the most heavily used plotting library in the Python community. In science and data science, it is definitely the most used. Groups of scientists rely on matplotlib, and since matplotlib turned to version 3 and dropped the python2 legacy, the community may demand stability. Current industry standard, as well as for serious scientific development, includes writing tests for the code, especially when using the rapidly changing frameworks. Matplotlib is trying to provide some testing environment, such as decorator
image_comparison, and this is great. But the issue that forbids sequential runs of multiple test cases is here.Currently, the documentation for
image_comparisonstates thatBut it is not comparing the images generated by test, rather all open figures.
As far as I can see (did not go to the source code yet), the decorator tries to compare all currently opened figures with the saved one. At the same time, the previous test leaves the figure open. I can propose three solutions for that:
plt.close('all')into the corresponding documentation example and change the docstring ofimage_comparison.I am ready to implement one of the above if this PR is likely to be accepted, maybe with the support from the core team if possible.
Best wishes,
Greg.
jklymak commentedon Aug 6, 2020
I suggest discourse because I think you need help setting up your testing environment, and this is not the appropriate venue for that.
tacaswell commentedon Aug 6, 2020
The way the test the work they have to collect all of the currently open figures (one could argue we should be returning the tuple of the figures we want tested but that is neither here nor there). The way we cope with this in our test suite (which uses these tools) is via
matplotlib/lib/matplotlib/testing/conftest.py
Lines 32 to 101 in d38e443
which is auto-used for all of the tests and takes care of making sure any global state that is touched is cleaned up and (implicitly) closing all of the figures via
plt.switch_backend.I think the fix here is to document importing that fixture into your conftest.py
SmirnGreg commentedon Aug 6, 2020
Dear @tacaswell,
thanks a lot, that is what I was looking for! It seems to me that when testing not the matplotlib itself, but own packages, the following import makes everything fine (as the fixture is applied to every test case):
No further setup is required with latest pip releases of pytest and matplotlib.
tacaswell commentedon Aug 6, 2020
@SmirnGreg Where would you have expected to find that information (and can you open a PR putting it there?) ?
timhoffm commentedon Aug 7, 2020
I'm wondering if our testing functions are intended to be public for the end user. How does our internal testing compare to pytest-mpl? A pytest plugin seems to be the cleaner approach compared to importing from conftest.
dopplershift commentedon Aug 7, 2020
I would 100% say everyone else should use
pytest-mpland not matplotlib's own testing code. And honestly, if we had cycles to burn, I'd say we should migrate matplotlib itself topytest-mpl.SmirnGreg commentedon Aug 11, 2020
@timhoffm @dopplershift
thank you for the discussion!
matplotlib. I went to the matplotlib.org and typed "test" in the search box, and the first two link point to thematplotlib.testingpackage. You are right, it is written there that it is for developers, but users also might be interested in such functionality. Also,matplotlib.testingis provided alongside the mainmatplotlibpackage not only in the developer version. I assumed that it is a valid way for testing and tries to run an example, an failed.a. It still depends on
matplotlib.testing.compare.compare_images. It has some wrapper around it, but..b. I do find the original testing decorators more convenient. First, and the most important, I like the way they store the figures in the folder named after the file with the test case.
c. I also like that the decorator saves the output figure even if the tolerance test has passed -- that is helpful if the tolerance value was set to high, but I still want to check by eye time to time.
d. Its current release version has a leading 0. Does that mean that the API can change dramatically over time? I wanted a more stable solution.
e. The decorator API is much richer than that of
pytest-mpl.Based on the comment by @tacaswell, I would propose one or more of the following:
pytest-mplis suggested for testing.matplotlibtesting environment, and that one can import a fixturefrom matplotlib.testing.conftest import mpl_test_settingsto make it close the figure after the test and roll back to the initial state.What would you say?
timhoffm commentedon Nov 6, 2020
I'd go with the three documentation bullets.
hs2361 commentedon Nov 9, 2020
@SmirnGreg just to be clear, adding
fixes the problem with the tests failing? If so, I'll go ahead and add in this line to the example in the docs as well, in the places that you suggested.
@timhoffm Would that be alright?
timhoffm commentedon Nov 9, 2020
Um, not the pytest expert here, but I think it's sufficient to have
mpl_test_settingsimported in oneconftest.pyper project. Since that's already done in the matplotlib codebase andmatplotlib.testingis not to be used by external users, no documentation is needed concerning the import.tacaswell commentedon Nov 10, 2020
This won't work as pytest needs to find the fixture before it finds the test. Might make sense to add a
plt.close('all')to the test decorator though?Yes, pytest can find fixtures either in a conftest.py or at the module level. If you want it everywhere in a package, put it in conftest.py, if you only want it in a single test module import it at the module level. Either will work, depends on how aggressively you want to reset Matplotlib's global state.
For better-or-worse I think users have discovered our testing module and are using it so we should document it, however I would not add it to the example as the example is about how to write tests for our test suite.
I do think that pytest-mpl is a better option (and I agree with @dopplershift that if we had effort we would migrate to depending on them).