2015-01-14 4 views
20

Ich arbeite an einem Python (2.7) -Programm, das viele verschiedene Matplotlib-Zahlen produziert (die Daten sind nicht zufällig). Ich bin bereit, einen Test (mit Unittest) durchzuführen, um sicher zu sein, dass die generierten Zahlen korrekt sind. Zum Beispiel speichere ich die erwartete Zahl (Daten oder Bild) irgendwo, ich führe meine Funktion aus und vergleiche das Ergebnis mit der Referenz. Gibt es eine Möglichkeit, dies zu tun?Wie kann ich Komponententests für Code schreiben, der matplotlib verwendet?

Antwort

19

In meinem experience bringen Bildvergleichstests mehr Ärger als sie wert sind. Dies ist insbesondere dann der Fall, wenn Sie eine kontinuierliche Integration über mehrere Systeme hinweg (wie TravisCI) durchführen möchten, die leicht unterschiedliche Schriftarten oder verfügbare Zeichnungs-Backends haben können. Es kann sehr viel Arbeit sein, die Tests auch dann bestehen zu lassen, wenn die Funktionen einwandfrei funktionieren. Darüber hinaus erfordert das Testen dieser Methode das Speichern von Bildern in Ihrem Git-Repository, was schnell zu einem Aufblähen des Repository führen kann, wenn Sie den Code häufig ändern.

Ein besserer Ansatz ist meiner Meinung nach zu (1) gehe davon aus, dass Matplotlib die Figur tatsächlich korrekt zeichnen wird, und (2) numerische Tests mit den von den Plotfunktionen zurückgegebenen Daten durchführt. (. Sie können auch immer, diese Daten innerhalb des Axes Objekt finden, wenn Sie wissen, wo sie suchen müssen)

Zum Beispiel, sagen Sie eine einfache Funktion wie folgt testen wollen:

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) 

Ihr Unit-Test könnte dann aussehen wie

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)) 

oder äquivalent

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

Mit Ihrer Lösung müssen Sie das Ergebnis der Handlung zurückgeben. Gibt es eine Möglichkeit, dasselbe zu tun, ohne es zurückgeben zu müssen? Indem man die Feige "fängt"? Meine Plot-Funktionen "kehren" für jetzt leer, wenn es der einzige Weg ist, den ich ändern kann, aber das möchte ich nicht. –

+0

Sehen Sie sich das zweite Beispiel an. Es zeigt, wie Sie das Achsenobjekt inspizieren können, um alle Daten zu finden, mit denen das Diagramm gezeichnet wird. – mwaskom

+0

@mwaskom Irgendwelche Erfahrungen mit Tests gegen eine mpl.collection, die du explizit hinzugefügt hast? z.B. 'ax.add_collection (PatchCollection (...))'? –

9

Matplotlib hat eine testing infrastructure. Zum Beispiel:

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') 

Vom docs:

Das erste Mal in diesem Test durchgeführt wird, wird es keine Basisbild zu vergleichen gegen, so schlägt der Test fehl. Kopieren Sie die Ausgangsbilder (in in diesem Fall result_images/test_category/stures_axes_positions. *) In das korrekte Unterverzeichnis des Ausgangsverzeichnisbaums im Quellverzeichnis (in diesem Fall lib/matplotlib/tests/baseline_images/test_category). Wenn die Tests erneut ausgeführt werden, sollten sie jetzt bestanden werden.

+0

ich Ihren Weg versucht haben, und ich habe eine E Fehler. In matplotlib.testing.decorators gibt es einen "import matplotlib.tests", der einen ImportError erzeugt: Kein Modul namens tests. Ich habe einige Nachforschungen gemacht und in der Tat gibt es kein Testmodul in meinen Matplolib-Dateien und die Dokumentation sagt leise darüber. Weiß jemand, wie man es löst? –

Verwandte Themen