【问题标题】:How to mock pyplot.show in python (to prevent showing plots)如何在 python 中模拟 pyplot.show(以防止显示绘图)
【发布时间】:2018-12-27 23:51:19
【问题描述】:

我想断言 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()

#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 设置返回值。当我使用调试器时,模型类中的 matplotlib 不会通过 Mock 对象传播,所以我不确定如何模拟 matplotlib 模块或其成员函数。是否可以模拟 matplotlib?

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

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

【问题讨论】:

    标签: python unit-testing matplotlib mocking python-unittest


    【解决方案1】:

    当您调用 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 的引用仍未被模拟。

    【讨论】:

    • 这对于python中的一般模拟非常有用。我没有考虑到未模拟的引用是在补丁之前的导入时建立的。谢谢!
    猜你喜欢
    • 2021-05-24
    • 2011-11-23
    • 2014-10-17
    • 1970-01-01
    • 1970-01-01
    • 2019-04-08
    • 2011-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多