Generate multiple tests based on a function's output

Viewed 75

I have a function (my_plots()) that creates and displays multiple figures with matplotlib. I want to test these figures using the pytest-mpl plugin, which appears to need a single figure per test. https://github.com/matplotlib/pytest-mpl

By using monkeypatch I can generate a sequence of figures (get_figures()) and pass these through pytest.mark.parametrize. This works for the actual image comparison but, because I'm running the original function outside the test, it is unaffected by other useful pytest features such as pytest.mark.filterwarnings (or any fixtures I add).

Is there a better way to generate the tests?

import warnings

import matplotlib.pyplot as plt
import pytest


def my_plots():
    plt.figure()
    plt.plot([0, 1])
    plt.show()
    
    plt.figure()
    warnings.warn("bad things may happen")
    plt.plot([0, 2])
    plt.show()
    

def get_figures():
    def no_show():
        pass
    
    with pytest.MonkeyPatch.context() as mp:
        mp.setattr(plt, "show", no_show)
        my_plots()
        
    for fig_num in plt.get_fignums():
        yield plt.figure(fig_num)
        

@pytest.mark.filterwarnings("error")        
@pytest.mark.mpl_image_compare
@pytest.mark.parametrize("fig", get_figures())
def test_my_plots(fig):
    return fig
1 Answers

Inspired by this GitHub comment, here is the best I've come up with so far. Note I can't use the monkeypatch fixture within the get_figures fixture because it has a narrower scope.

import warnings

import matplotlib.pyplot as plt
import pytest


def my_plots():
    plt.figure()
    plt.plot([0, 1])
    plt.show()
    
    plt.figure()
    warnings.warn("bad things may happen")
    plt.plot([0, 2])
    plt.show()
    

@pytest.fixture(scope="module")
def get_figures():
    def no_show():
        pass
    
    with pytest.MonkeyPatch.context() as mp:
        mp.setattr(plt, "show", no_show)
        my_plots()
        
    figs = [plt.figure(fig_num) for fig_num in plt.get_fignums()]
    return figs
        

@pytest.mark.filterwarnings("error")        
@pytest.mark.mpl_image_compare
@pytest.mark.parametrize("fig_index", [0, 1])
def test_my_plots(fig_index, get_figures):
    return get_figures[fig_index]
Related