我想为交互式matplotlib绘图编写unittest。我的问题是,我找不到模拟按键或鼠标按键事件的好方法。我知道pyautogui,但我必须关心matplotlib窗口在屏幕上的位置(例如,在TravisCI上,如果不配置它,我怀疑它是否能正常工作(。我试过查看Matplotlib的单元测试,但还找不到任何有用的东西。最好的解决方案是在不涉及GUI部分的情况下在代码中触发事件,但到目前为止我无法解决它
我想出的最简单的例子如下。可以使用i
键在绘图上标记点。这里我要测试的函数是on_press
。
import numpy as np
import matplotlib.pyplot as plt
class PointRecorder:
def __init__(self, x, y):
plt.ion()
self.figure = plt.figure()
self.cid = self.figure.canvas.mpl_connect("key_press_event", self.on_press)
self.x = x
self.y = y
self.x_points, self.y_points = [2], [0.5]
plt.plot(self.x, self.y, "r")
self.pts, = plt.plot(self.x_points, self.y_points, "ko", markersize=6, zorder=99)
plt.show(block=True)
def on_press(self, event):
ix, iy = event.xdata, event.ydata
if event.inaxes is None:
return
if event.key == 'i':
self.x_points.append(ix)
self.y_points.append(iy)
self.pts.set_data(self.x_points, self.y_points)
if self.pts.stale:
self.figure.canvas.draw_idle()
def get_data(self):
return self.pts.get_data()
if __name__ == "__main__":
x = np.linspace(0, 6, 100)
y = np.sin(x)
graph = PointRecorder(x, y)
print(*graph.get_data())
你能建议如何正确测试这种功能吗?
我不是单元测试专家,但我的猜测是,您需要实例化一个Event对象(在key_press_Event的情况下,它应该是一个KeyEvent(,并从测试代码中调用graph.on_press(event)
根据建议,解决方案如下:首先定义一个空白事件。
from unittest import mock
def mock_event(xdata, ydata, button, key, fig, canvas, inaxes=True):
event = mock.Mock()
event.button = button
event.key = key
event.xdata, event.ydata = xdata, ydata
event.inaxes = inaxes
event.fig = fig
event.canvas = canvas
event.guiEvent = None
event.name = 'MockEvent'
return event
然后初始化上面的PointRecorder
类。然后,定义一个适合PointRecorder.on_press
方法的mock_event
。还有patch
plt.show
,以避免阻塞执行。
@mock.patch("matplotlib.pyplot.show")
def test_insert(mock_show):
x, y = np.arange(100), np.arange(100)
obj = PointRecorder(x, y)
mck = mock_event(xdata=50, ydata=40, button="i", key="i", fig=obj.figure, canvas=obj.figure.canvas, inaxes=True)
obj.on_clicked(event=mck)
a, b = obj.get_data()
np.testing.assert_array_equal(a, np.array([2, 50])) # 2 was originally in the __init__
np.testing.assert_array_equal(b, np.array([0.5, 40])) # 0.5 was originally in the __init__
mock_show.assert_called()