2015-01-14 9 views
20

Sto lavorando su un programma python (2.7) che produce un sacco di figure matplotlib diverse (i dati non sono casuali). Sono disposto a implementare alcuni test (utilizzando l'unittest) per essere sicuri che le cifre generate siano corrette. Ad esempio, memorizzo la figura attesa (dati o immagine) in qualche punto, eseguo la mia funzione e confronta il risultato con il riferimento. C'è un modo per fare questo ?Come posso scrivere test unitari contro codice che usa matplotlib?

risposta

19

Nel mio experience, i test di confronto immagini finiscono per portare più problemi di quanti ne valgano. Questo è particolarmente vero se si desidera eseguire l'integrazione continua su più sistemi (come TravisCI) che potrebbero avere font leggermente diversi o backend di disegno disponibili. Può essere molto impegnativo mantenere il test superato anche quando le funzioni funzionano perfettamente. Inoltre, il test in questo modo richiede il mantenimento di immagini nel repository git, che può rapidamente portare al deposito in modo eccessivo se si modifica il codice spesso.

Un approccio migliore secondo me è (1) presupporre che matplotlib stia effettivamente disegnando correttamente la figura e (2) esegua test numerici contro i dati restituiti dalle funzioni di tracciamento. (. È inoltre possibile sempre trovare questi dati all'interno dell'oggetto Axes se si sa dove guardare)

Ad esempio, dire che si vuole testare una semplice funzione come questa:

import numpy as np 
import matplotlib.pyplot as plt 
def plot_square(x, y): 
    y_squared = np.square(y) 
    return plt.plot(x, y_squared) 

vostro unit test potrebbe quindi assomigliare

def test_plot_square1(): 
    x, y = [0, 1, 2], [0, 1, 2] 
    line, = plot_square(x, y) 
    x_plot, y_plot = line.get_xydata().T 
    np.testing.assert_array_equal(y_plot, np.square(y)) 

o, equivalentemente,

def test_plot_square2(): 
    f, ax = plt.subplots() 
    x, y = [0, 1, 2], [0, 1, 2] 
    plot_square(x, y) 
    x_plot, y_plot = ax.lines[0].get_xydata().T 
    np.testing.assert_array_equal(y_plot, np.square(y)) 
+1

Con la soluzione, è necessario restituire il risultato della trama. C'è un modo per fare la stessa cosa senza doverlo restituire? "Catturando" il fico? La mia trama funziona "return" void per ora, se è l'unico modo in cui posso cambiare ma mi piacerebbe non farlo. –

+0

Guarda il secondo esempio, mostra come puoi introspettare l'oggetto degli assi per trovare tutti i dati che vengono utilizzati per disegnare la trama al suo interno. – mwaskom

+0

@mwaskom Qualche esperienza nell'esecuzione di test su mpl.collection che hai aggiunto esplicitamente? per esempio. 'Ax.add_collection (PatchCollection (...))'? –

9

Matplotlib ha un testing infrastructure. Per esempio:

import numpy as np 
import matplotlib 
from matplotlib.testing.decorators import image_comparison 
import matplotlib.pyplot as plt 

@image_comparison(baseline_images=['spines_axes_positions']) 
def test_spines_axes_positions(): 
    # SF bug 2852168 
    fig = plt.figure() 
    x = np.linspace(0,2*np.pi,100) 
    y = 2*np.sin(x) 
    ax = fig.add_subplot(1,1,1) 
    ax.set_title('centered spines') 
    ax.plot(x,y) 
    ax.spines['right'].set_position(('axes',0.1)) 
    ax.yaxis.set_ticks_position('right') 
    ax.spines['top'].set_position(('axes',0.25)) 
    ax.xaxis.set_ticks_position('top') 
    ax.spines['left'].set_color('none') 
    ax.spines['bottom'].set_color('none') 

Dal docs:

La prima volta che si esegue il test, non ci sarà alcuna immagine di base per confrontare contro, quindi il test avrà esito negativo. Copia le immagini di output (in in questo caso result_images/test_category/spines_axes_positions. *) Nella sottodirectory corretta dell'albero baseline_images nella directory di origine (in questo caso lib/matplotlib/tests/baseline_images/test_category). Quando riesegui i test, dovrebbero ora passare.

+0

ho provato la tua strada e ho avuto una e rror. In matplotlib.testing.decorators esiste un "import matplotlib.tests" che produce un ImportError: nessun modulo chiamato test. Ho fatto qualche ricerca e in effetti non esiste un modulo di test nei miei file matplolib e la documentazione dice pochissimi su di esso. Qualcuno sa come risolverlo? –

Problemi correlati