I'm switching from PySide2 to PyQt5 and am getting a TypeError in one of my tests.
The test checks for whether exit_action actually calls the close method.
bug.py
# bug.py
from PySide2 import QtCore, QtWidgets, QtGui
# from PyQt5 import QtCore, QtWidgets, QtGui
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.exit_action = QtWidgets.QAction('&Exit', self)
self.exit_action.triggered.connect(self.close)
self.file_menu = self.menuBar().addMenu('&File')
self.file_menu.addAction(self.exit_action)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
main_window = MainWindow()
main_window.show()
app.exec_()
test_bug.py
# test_bug.py
# python3 -m unittest test_bug.TestMainWindowExitAction
import bug
import unittest
import unittest.mock
from PySide2 import QtCore, QtWidgets, QtGui, QtTest
# from PyQt5 import QtCore, QtWidgets, QtGui, QtTest
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication([])
class TestMainWindowExitAction(unittest.TestCase):
def setUp(self):
self.main_window = bug.MainWindow()
def test_exit_action_trigger_closes_application_with_mock(self):
close_mock = unittest.mock.MagicMock()
self.main_window.closeEvent = close_mock
self.main_window.exit_action.triggered.emit()
close_mock.assert_called_once()
# def test_exit_action_trigger_closes_application_without_mock(self):
# self.called=0
# def my_close(arg):
# print(f"HERE {arg}")
# self.called+=1
# self.main_window.closeEvent = my_close
# self.main_window.exit_action.triggered.emit()
# self.assertEqual(self.called, 1)
It works great with Pyside2. With PyQt5, it gives an error:
TypeError: invalid argument to sipBadCatcherResult()
Aborted
The error happens when exit_action.triggered is emitted.
If I instead run test_exit_action_trigger_closes_application_without_mock, everything runs fine:
HERE <PyQt5.QtGui.QCloseEvent object at 0x7f73488e75e0>
.
----------------------------------------------------------------------
Ran 1 test in 0.005s
OK
It's not clear to me what the "right" way to test this is. Even though the nested def works, it's not obvious how it works (although I do understand it). If I mock close instead of closeEvent, the mock isn't called during testing, despite the action closing the application when I manually run it. It's not clear to me if the problem is with PyQt5 (i.e. sip) or with unittest.mock or if everything is fine and I've just confused myself.