簡體   English   中英

如何針對使用 matplotlib 的代碼編寫單元測試?

[英]How can I write unit tests against code that uses matplotlib?

我正在開發一個 python (2.7) 程序,該程序生成許多不同的 matplotlib 圖(數據不是隨機的)。 我願意實施一些測試(使用 unittest)以確保生成的數字是正確的。 例如,我將預期的圖形(數據或圖像)存儲在某個位置,然后運行我的函數並將結果與​​參考進行比較。 有沒有辦法做到這一點 ?

根據我的經驗,圖像比較測試最終帶來的麻煩多於其價值。 如果您想跨多個系統(如 TravisCI)運行持續集成,這些系統可能具有略微不同的字體或可用的繪圖后端,則尤其如此。 即使功能完全正常工作,也需要做很多工作來保持測試通過。 此外,以這種方式進行測試需要將圖像保存在您的 git 存儲庫中,如果您經常更改代碼,這會很快導致存儲庫膨脹。

在我看來,更好的方法是 (1) 假設 matplotlib 將實際正確繪制圖形,並且 (2) 對繪圖函數返回的數據運行數值測試。 (如果您知道在哪里查看,您也可以在Axes對象中找到這些數據。)

例如,假設您要測試這樣的簡單函數:

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)

你的單元測試可能看起來像

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

或者,等效地,

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

您還可以使用unittest.mock來模擬matplotlib.pyplot並檢查是否對它進行了具有適當參數的適當調用。 假設您在module.py有一個plot_data(data)函數(假設它位於package/src/ )您想要測試,它看起來像這樣:

import matplotlib.pyplot as plt

def plot_data(x, y, title):
    plt.figure()
    plt.title(title)
    plt.plot(x, y)
    plt.show()

為了在您的test_module.py文件中測試此功能,您需要:

import numpy as np

from unittest import mock
import package.src.module as my_module  # Specify path to your module.py


@mock.patch("%s.my_module.plt" % __name__)
def test_module(mock_plt):
    x = np.arange(0, 5, 0.1)
    y = np.sin(x)
    my_module.plot_data(x, y, "my title")

    # Assert plt.title has been called with expected arg
    mock_plt.title.assert_called_once_with("my title")

    # Assert plt.figure got called
    assert mock_plt.figure.called

這將檢查是否使用參數my title調用了title方法,以及plot_dataplt對象的plot_data內調用了figure方法。

更詳細的解釋:

@mock.patch("module.plt")裝飾器“修補”導入到module.pyplt模塊,並將其作為mock對象 ( mock_plt ) 作為參數注入到test_module中。 這個模擬對象(作為mock_plt傳遞)現在可以在我們的測試中使用來記錄plot_data (我們正在測試的函數)對plt所做的一切 - 那是因為plot_dataplt的所有調用現在都將在我們的模擬中進行對象。

此外,除了assert_call_once_with之外,您可能還想使用其他類似的方法,例如assert_not_calledassert_called_once等。

Matplotlib 有一個測試基礎設施 例如:

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

文檔

第一次運行此測試時,將沒有可比較的基線圖像,因此測試將失敗。 將輸出圖像(在本例中為 result_images/test_category/spines_axes_positions.*)復制到源目錄中的基線圖像樹的正確子目錄(在本例中為 lib/matplotlib/tests/baseline_images/test_category)。 重新運行測試時,它們現在應該通過了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM