2015-01-14 11 views
20

Pracuję nad programem Pythona (2.7), który produkuje wiele różnych postaci matplotlib (dane nie są przypadkowe). Jestem gotów przeprowadzić jakiś test (używając unittest), aby mieć pewność, że wygenerowane liczby są poprawne. Na przykład przechowuję oczekiwaną wartość (dane lub obraz) w pewnym miejscu, uruchamiam moją funkcję i porównuję wynik z referencją. Czy jest jakiś sposób na zrobienie tego?Jak mogę napisać testy jednostkowe na kodzie używającym matplotlib?

Odpowiedz

19

W moich testach porównawczych obrazy przynoszą więcej problemów niż są warte. Dzieje się tak zwłaszcza w przypadku ciągłej integracji wielu systemów (np. TravisCI), które mogą mieć nieco inne czcionki lub dostępne backend rysunkowy. Wykonanie testów może być bardzo trudne, nawet jeśli funkcje działają poprawnie. Ponadto testowanie w ten sposób wymaga przechowywania obrazów w repozytorium git, co może szybko doprowadzić do nadpisania repozytorium, jeśli często zmieniasz kod.

Lepszym podejściem moim zdaniem jest (1) założenie, że matplotlib zamierza właściwie narysować figurę i (2) przeprowadzić testy numeryczne względem danych zwracanych przez funkcje kreślenia. (. Można też zawsze znaleźć te dane wewnątrz obiektu Axes, jeśli wiesz, gdzie szukać)

Załóżmy, że chcesz przetestować prostą funkcję tak:

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) 

test jednostka może następnie wyglądać

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

lub, równoważnie,

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

Za pomocą swojego rozwiązania musisz zwrócić wynik wydruku. Czy istnieje sposób na to samo, bez konieczności zwrotu? Przez "łapanie" figi? Moja fabuła funkcjonuje "na próżno" na razie, jeśli to jedyny sposób, w jaki mogę się zmienić, ale nie chciałbym. –

+0

Spójrz na drugi przykład, pokazuje, jak możesz introspekować obiekt osi, aby znaleźć wszystkie dane, które są używane do narysowania w nim wątku. – mwaskom

+0

@mwaskom Jakieś doświadczenie podczas uruchamiania testów przeciwko mpl.collection, które dodałeś jawnie? na przykład 'ax.add_collection (PatchCollection (...))'? –

9

Program Matplotlib ma numer testing infrastructure. Na przykład:

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

Z docs:

Pierwszy raz ten test jest uruchamiany, nie będzie bazowy obraz do porównać przeciw, więc test nie powiedzie się. Skopiuj obrazy wyjściowe (w tej sprawie result_images/test_category/spines_axes_positions. *) Do poprawnego podkatalogu drzewa baseline_images w katalogu źródłowym (w tym przypadku lib/matplotlib/tests/baseline_images/test_category). Przy ponownym testowaniu powinny teraz przejść.

+0

próbowałem na swój sposób, a ja dostałem E rror. W matplotlib.testing.decorators znajduje się "import matplotlib.tests", który generuje wyjątek ImportError: No module named tests. Zrobiłem kilka badań i rzeczywiście nie ma modułu testów w moich plikach matplolib, a dokumentacja mówi cicho o tym niewiele. Czy ktoś wie, jak go rozwiązać? –