26

I'm building a PySide 1.1.0-based application, and have been looking for good examples to look at for unit and functional testing my application. I want to be able to do functional testing of the UI (simulating clicks, key presses, etc), unit testing of UI slots that alter the layout of the UI (presumably using a partially-mocked sender and receiver), as well as unit testing of code that involves widgets, but without requiring any windows to be rendered.

As one example, I dynamically create submenus of one menu in the menubar when an item is added to a model (QAbstractItemModel-derived object) that provides data to a QTreeView. The model and submenu must stay in sync, so I want to be able to write a unit test that submits data to the controller that manages the model and submenu, and asserts that both the model and submenu were properly updated.

I would prefer to NOT have to set up a QApplication in my test code if I can avoid it. I also would like to not have to display any windows when I only care about validating data structures in widgets, not their visualization.

I can't find anything of suitable value at http://www.pyside.org or in my Google searches. Does anyone have any experience or know of good sample code that I should look at?

4
  • I'm also highly intereseted in a solution for this, as I am facing the exact same problems Commented Aug 17, 2012 at 13:23
  • 1
    Have you seen: johnnado.com/pyqt-qtest-example It is PyQt, but pretty much the same. Commented Mar 25, 2015 at 1:15
  • @eric That link is broken. Is there any other link? Commented Aug 5, 2021 at 17:38
  • web.archive.org/web/20160303001130/johnnado.com/… Commented Aug 5, 2021 at 23:17

2 Answers 2

44

I've been playing around a bit now with unit-testing pyside code and came to the conclusion that combining python's unittest module with qt's QTest module works pretty good.

You will have to have a QApplication object instantiated, but you do not need to run its exec_ method, because you don't need the event loop to be running.

Here is an example on how I test if a QCheckBox in a dialog does what it is supposed to do:

class Test_PwsAddEntryDialog(TestCase): """Tests the class PwsAddEntryDialog.""" def test_password_strength_checking_works(self): """Tests if password strength checking works, if the corresponding check box is checked. """ d = PwsAddEntryDialog() # test default of internal flag self.assertFalse(d.testPasswordStrength) # type something QTest.keyClicks(d.editSecret, "weak", 0, 10) # make sure that entered text is not treated as a password self.assertEqual(d.labelPasswordStrength.text(), "") # click 'is password' checkbox QTest.mouseClick(d.checkIsPassword, Qt.LeftButton) # test internal flag changed self.assertTrue(d.testPasswordStrength) # test that label now contains a warning self.assertTrue(d.labelPasswordStrength.text().find("too short") > 0) # click checkbox again QTest.mouseClick(d.checkIsPassword, Qt.LeftButton) # check that internal flag once again changed self.assertFalse(d.testPasswordStrength) # make sure warning disappeared again self.assertEqual(d.labelPasswordStrength.text(), "") 

This completely works off-screen, involves clicking widgets and typing text in a QLineEdit.

Here is how I test a (rather simple) QAbstractListModel:

class Test_SectionListModel(TestCase): """Tests the class SectionListModel.""" def test_model_works_as_expected(self): """Tests if the expected rows are generated from a sample pws file content. """ model = SectionListModel(SAMPLE_PASSWORDS_DICT) l = len(SAMPLE_PASSWORDS_DICT) self.assertEqual(model.rowCount(None), l) i = 0 for section in SAMPLE_PASSWORDS_DICT.iterkeys(): self.assertEqual(model.data(model.index(i)), section) i += 1 

I hope this helps a littlebit.

Sign up to request clarification or add additional context in comments.

Comments

4

In my case, I was getting an error 'QPixmap: Must construct a QApplication before a QPaintDevice'.

If you need to have a QApplication instance for your tests (eg use QPixmap), here's one way to do it. Just create a singleton so that you are ensured one and only one QApplication instance.

This is buried as a helper for tests in the PySide source.

import unittest from PySide.QtGui import QApplication _instance = None class UsesQApplication(unittest.TestCase): '''Helper class to provide QApplication instances''' qapplication = True def setUp(self): '''Creates the QApplication instance''' # Simple way of making instance a singleton super(UsesQApplication, self).setUp() global _instance if _instance is None: _instance = QApplication([]) self.app = _instance def tearDown(self): '''Deletes the reference owned by self''' del self.app super(UsesQApplication, self).tearDown() 

and then subclass UsesQApplication

from PySide import QtGui class Test(UsesQApplication): def setUp(self): #If you override setup, tearDown, make sure #to have a super call super(TestFilterListItem, self).setUp() def tearDown(self): super(TestFilterListItem, self).tearDown() def testName(self): pix = QtGui.QPixmap(20,20) self.assertTrue(True) 

hope this helps

2 Comments

I just do if QtGui.qApp == None: QtGui.QApplication([]) at the beginning of every test module which uses QtGui.
For PySide2 and greater, "The QApplication object is accessible through the instance() function that returns a pointer equivalent to the global qApp pointer." So, if not QtWidgets.QApplication.instance(): APP = QtWidgets.QApplication([]) doc.qt.io/qtforpython-5/PySide2/QtWidgets/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.