如何针对使用matplotlib的代码编写单元测试?

时间:2015-01-14 16:44:51

标签: python matplotlib python-unittest

我正在研究一个python(2.7)程序,它产生了许多不同的matplotlib数字(数据不是随机的)。我愿意实施一些测试(使用unittest)以确保生成的数字是正确的。例如,我将预期的图形(数据或图像)存储在某个地方,我运行我的函数并将结果与​​参考进行比较。有没有办法做到这一点?

3 个答案:

答案 0 :(得分:27)

在我的experience中,图像对比测试最终会带来比它们更值得的麻烦。如果您希望在多个系统(如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))

答案 1 :(得分:11)

Matplotlib有一个testing infrastructure。例如:

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

来自docs

  

第一次运行此测试时,将没有基线图像   比较,所以测试将失败。复制输出图像(in   这种情况下result_images / test_category / spines_axes_positions。*)来了   source目录中的baseline_images树的正确子目录   (在本例中为lib / matplotlib / tests / baseline_images / test_category)。重新运行测试时,它们现在应该通过。

答案 2 :(得分:1)

您还可以使用unittest.mock模拟matplotlib.pyplot并检查是否对它进行了适当的调用并带有适当的参数。假设您要测试的plot_data(data)中有一个module.py函数,它看起来像这样:

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 mypyplot as my_module


@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 mock_plt.title.call_args_list[0][0][0] == "my title"
    assert mock_plt.figure.called

这将检查是否使用参数title调用了my title方法,并且是否在figure内部调用了plot_data方法。