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

gla*_*nix 5 python unit-testing mocking matplotlib python-unittest

我想断言 matplotlib.pyplot.show 在我的类的方法之一中被调用,但我不希望该方法在单元测试期间实际调用 show() 并在外部窗口上绘图。无论我如何尝试模拟 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()
Run Code Online (Sandbox Code Playgroud)
#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()
Run Code Online (Sandbox Code Playgroud)

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

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

编辑:我能够通过将补丁装饰更改为 来使其工作 @patch('matplotlib.pyplot.show'),但我仍然不确定为什么这有效而我之前的尝试没有。

Tho*_*ers 6

当您调用 时patch,matplotlib模块会在内存中进行修补,因此任何访问其子元素的新尝试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))
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

您会注意到show我们在bla模块中获得的引用保持未模拟。