Issue
I am looking to implement unittests for PySide UI and can't figure out how to properly assert that the functions connected to ui elements got called.
So far I've tried different ways to import the module I am writing the test for. Different ways of mocking the function
../app/ui.py
import sys
from PySide.QtGui import *
from PySide.QtCore import *
def btn1_callback():
print("1")
class Window(QWidget):
def __init__(self, *args, **kwargs):
super(Window, self).__init__(*args, **kwargs)
layout = QVBoxLayout()
self.btn1 = QPushButton("Foo")
self.btn1.clicked.connect(btn1_callback)
layout.addWidget(self.btn1)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
app.exec_()
../tests/test_ui.py
import sys
import unittest
import mock
from PySide.QtGui import *
from PySide.QtCore import *
from PySide.QtTest import QTest
import app.ui
application = QApplication(sys.argv)
class UiTest(unittest.TestCase):
def setUp(self):
self.ui = app.ui.Window()
@mock.patch.object(app.ui, "btn1_callback")
def test_btn1(self, callback):
QTest.mouseClick(self.ui.btn1, Qt.LeftButton)
self.assertTrue(callback.called)
if __name__ == '__main__':
unittest.main()
Running python -m unittest discover tests
will run the 1 test and fail saying that the function was not called. It will however have printed "1" to the console so I know the QTest did manage to cause the callback to be executed.
Solution
patch.object
basically modifies what access to the name app.ui.btn1_callback
resolves to. However, you aren't calling it until after app.ui.Window()
has already gotten its own reference to that function. That reference isn't affected by the patch. You have to instantiate the UI and run the test while the patch is still in effect:
class UiTest(unittest.TestCase):
def setUp(self):
p = mock.patch.object(app.ui, "btn1_callback")
self.addCleanup(p.stop)
self.callback = p.start()
self.ui = app.ui.Window()
def test_btn1(self):
QTest.mouseClick(self.ui.btn1, Qt.LeftButton)
self.assert(self.callback.called)
Now startup
is responsible for enabling the patch, as well as scheduling its eventual disabling. Window()
and the test itself run in an environment where the callback has already been patched.
(Using addCleanup
is safer than defining tearDown
to call p.stop()
because it ensures the patch is stopped even if tearDown
isn't called for any reason.)
Answered By - chepner
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.