I'm trying to figure out how to mock matplotlib
's plt.savefig
inside a class to test if my GUI's button is actually saving a figure. For simplicity's sake, I will not include the GUI, but only the part I am testing. Here is the code structure:
I'm using pytest
to run the test file.
This code is in file1
, and it is the code I want to test:
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
class MyClass:
def __init__(self):
pass
def to_test(self):
if hasattr(self, 'attribute'): # The self.attribute variable would be defined in another function, but I do not want to call that function as part of the test
filepath= 'the_file_path'
self.attribute.savefig(filepath)
def invoke(self): # This is the simplified command the tk.Button of my GUI would call
self.to_test()
My goal is to make sure that whenever MyClass.invoke() is called, self.attribute.savefig() is also called
This is inside my test file
import unittest
from unittest.mock import patch, MagicMock
import sys
sys.path.append('path to directory')
from file1 import MyClass
@patch('file1.plt.savefig')
def test(mock_save):
myclass = MyClass()
myclass.attribute = MagicMock()
myclass.invoke()
assert mock_save.call_count == 1
Unfortunately, when I run this test, the following error occurs
E AssertionError: assert 0 == 1
E + where 0 = <MagicMock name='savefig' id='4471947984'>.call_count
Does anyone know why?
The reason why this is happening is that when you assign a MagicMock
to .attribute
, it has nothing to do with the Mock
that your @patch
operator has used to override pyplot.savefig
. What you can instead do is get rid of the patch
and test that the savefig
method on the manually-assigned MagicMock
is called using assert_called_once
. Here's an example from the Python official docs:
>>> mock = Mock()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_once()
>>> mock.method()
<Mock name='mock.method()' id='...'>
>>> mock.method.assert_called_once()
Traceback (most recent call last):
...
AssertionError: Expected 'method' to have been called once. Called 2 times.
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.assert_called_once
The reason why this happens is that Mock
objects create all called methods and attributes as you call them, so when you call attribute.savefig
the object creates a new attribute which is itself a callable Mock, and all calls are registered against that.