I want to assert that matplotlib.pyplot.show was called in one of my class's methods, but I don't want the method to actually call show() and plot on an external window during the unit test. No matter how I try to mock matplotlib, it doesn't seem to work. I'll list the important parts of my code below:
#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()
I also tried setting a return value for mock_plot.show. When I use the debugger, matplotlib within the Model class is not propagated with a Mock object, so I am unsure how to mock the matplotlib module or its member functions. Is it possible to mock matplotlib?
Any ideas for how to properly mock matplotlib in unit tests (mainly to prevent displaying plots)?
EDIT: I was able to get it to work by changing the patch decoration to
@patch('matplotlib.pyplot.show')
,
but I'm still unsure why this worked and my previous attempt didn't.
If we are in interactive mode, the plot might get displayed. To avoid the display of plot we use close() and ioff() methods.
plt.show(block=False) #this creates an empty frozen window. _ = raw_input("Press [enter] to continue.") if __name__ == '__main__': main()
When you call patch
, the matplotlib
module is patched in-memory so any new attempts to access it's child element pyplot
will reference the mock. However, the module that houses Model
already has a direct reference to matplotlib.pyplot
; it obtained that reference when you imported it. It won't go through the patched matplotlib
module again. When you patch show
directly, patch
will instead modify the shared pyplot
module.
We can illustrate this by also obtaining a direct reference to the original show
function on module load:
# 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 console:
>>> 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>
You'll note that the reference to show
that we obtained in the bla
module remains un-mocked.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With