Saving multiple figures in one test
gopalakrishna opened this issue · 5 comments
I need to test multiple figures that are generated by a method for correctness. Using @pytest.mark.mpl_image_compare does not enable me to do that as it does not save multiple figures. Any way to do that?
Right now my thought is having dummy tests that return these figures individually from a shared variable and validate it one by one in different tests
This plugin can only compare one figure per test. However, you should be able to use @pytest.mark.parametrize
to run the same test function multiple times with different input parameters, and compare to each of their figures.
Your solution is another common way to solve this, as you can define a function such as create_figure(...)
which generates and returns a figure. Inside individual test functions you can then do return create_figure(...)
to test the specific figure. Here's an example of something like this.
Thanks for the reply. I am trying something like the following
`
class TestPCA:
@classmethod
def setup_class(cls):
pca.run_pca_on_csv("./myfile.csv")
for i in plt.get_fignums():
plt.figure(i)
plt.savefig('figure%d.png' % i)
@pytest.mark.mpl_image_compare(baseline_dir='baseline_images')
def test_first_plot(self):
return plt.figure(1)
@pytest.mark.mpl_image_compare(baseline_dir='baseline_images')
def test_second_plot(self):
return plt.figure(2)
@pytest.mark.mpl_image_compare(baseline_dir='baseline_images')
def test_third_plot(self):
return plt.figure(3)
`
And, I see that savefig call saves all the images. But, when I run
pytest -s --mpl-generate-path=baseline_images
there is no image captured. I can see that the 3 tests are picked by pytest
` platform win32 -- Python 3.10.2, pytest-7.0.0, pluggy-1.0.0
Matplotlib: 3.5.1
Freetype: 2.6.1
rootdir: D:\Work\python_test_automation, configfile: setup.cfg, testpaths: test
plugins: mpl-0.13
collected 3 items
`
If I move the method test_(first/second/third)_plot outside the class the baseline images are captured. Sorry, to deviate from the topic of the issue but I am trying to get this working for the first time.
The figures may not be available in the tests due to this:
pytest-mpl/pytest_mpl/plugin.py
Lines 617 to 622 in e1bf8dd
Not 100% sure as I don't often use test classes. Does seem like a bug though.
Instead of the setup_class
, a pytest fixture outside of the class which returns a list of Figure
objects should hopefully work. (And scope it to the class or module so it doesn't recreate all the figures for every test method.)
Thanks for the reply. This is a bug indeed.
For my immediate need, I am using matplotlib.test now. There is another bug(or maybe intended feature) that all the figures are closed that does not allow me to use cached figures across tests. Matplotlib.tests allows specifying multiple images to compare against hence using that for now.
You're welcome. The core Matplotlib decorator is probably easiest if a single method/function produces multiple figures. This plugin is designed for one figure returned per test function, which makes reporting the results and generating a HTML report easier. The closing of figures is intended for convenient memory management, although in theory there could be an option to disable this if needed.
For future reference, I just had a quick look to see what the issue was. Looks like it's caused by how figure numbers are scoped in Matplotlib. It may be because the active figure manager when setup_class
is run is not the same as when test_*_plot
are run. (Possibly due to the comment I linked to earlier.) Manually linking them in a figs
attribute seems to solve this for me. The following code should therefore work.
class TestPCA:
@classmethod
def setup_class(cls):
pca.run_pca_on_csv("./myfile.csv")
cls.figs = [plt.figure(i) for i in (1, 2, 3)]
@pytest.mark.mpl_image_compare(baseline_dir='baseline_images')
def test_first_plot(self):
return self.figs[0]
@pytest.mark.mpl_image_compare(baseline_dir='baseline_images')
def test_second_plot(self):
return self.figs[1]
@pytest.mark.mpl_image_compare(baseline_dir='baseline_images')
def test_third_plot(self):
return self.figs[2]