如何在 python 中模拟 pyplot.show(以防止显示情节)

How to mock pyplot.show in python (to prevent showing plots)

我想断言 matplotlib.pyplot.show 在我的 class 方法之一中被调用,但我不希望该方法实际调用 show() 并在外部 window 在单元测试期间。无论我如何尝试模拟 matplotlib,它似乎都不起作用。我将在下面列出我的代码的重要部分:

#In Model Test Class:
import Model
import matplotlib
import unittest
from unittest.mock import patch

class Model_Test(unittest.TestCase):

    @patch('matplotlib.pyplot')
    def test_plot_data(self, mock_pyplot):
        model = Model()
        model.plot_data()
        mock_pyplot.show.assert_called_once()

#In Model Class:
import matplotlib.pyplot as plt
import pandas as pd

class Model(object):
    ... # (__init__ and other methods)
    def plot_data(self):
        self.dataframe.plot(y=self.dataframe.columns, subplots=False, figsize=(15, 8), fontsize=12)
        plt.title('Data for Model Version: ' + self.version, fontsize=20)
        plt.xlabel('timestamp', fontsize=12)
        plt.ylabel('value', fontsize=12)
        plt.show()

我还尝试为 mock_plot.show 设置 return 值。当我使用调试器时,Model class 中的 matplotlib 不会与 Mock 对象一起传播,因此我不确定如何模拟 matplotlib 模块或其成员函数。是否可以模拟 matplotlib?

关于如何在单元测试中正确模拟 matplotlib 的任何想法(主要是为了防止显示绘图)?

编辑:我可以通过将补丁装饰更改为 @patch('matplotlib.pyplot.show'), 但我仍然不确定为什么这样做有效而我之前的尝试却没有。

当您调用 patch 时,matplotlib 模块会在内存中进行修补,因此任何 new 都会尝试访问它的子元素 pyplot将引用模拟。但是,容纳 Model 的模块已经直接引用了 matplotlib.pyplot;它在您导入时获得了该参考。它不会再次通过修补的 matplotlib 模块。当您直接修补 show 时,patch 将修改共享的 pyplot 模块。

我们可以通过在模块加载时获取对原始 show 函数的直接引用来说明这一点:

# bla.py
#import numpy as np
import matplotlib.pyplot as plt

show = plt.show

class A:
    def __init__(self):
        print(type(plt))
        print(type(plt.show))
        print(type(show))

Python 控制台:

>>> from unittest.mock import patch
>>> from bla import A
>>> with patch('matplotlib.pyplot') as p:
...   A()
... 
<class 'module'>
<class 'function'>
<class 'function'>
<bla.A object at 0x7f232b98a400>
>>> with patch('matplotlib.pyplot.show') as p:
...   A()
... 
<class 'module'>
<class 'unittest.mock.MagicMock'>
<class 'function'>
<bla.A object at 0x7f232b98d550>

您会注意到,我们在 bla 模块中获得的对 show 的引用仍未被模拟。