2

I use PyQt5.6, Python 3.5 and Windows for a hobby project where I have an app with some actions (QAction) that have shortcuts set (for key combinations Ctrl-N and Ctrl-B, for example). I have written unit tests to verify that those actions can be triggered and they do what they should.

However, I am not able to find any working example of how to trigger an action using a shortcut that has been set when writing a unit test with QTest.

To support the question, this is the minimal application code:

import sys
from PyQt5 import QtGui, QtCore
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QLabel


########################################################################
class Window(QMainWindow):
    def __init__(self):
        super(Window, self).__init__()
        self.setGeometry(50, 50, 500, 300)

        action1 = QAction("&Action1", self)
        action1.setShortcut("Ctrl+U")
        action1.triggered.connect(self.update_label)

        self.label = QLabel('Empty', self)
        self.setCentralWidget(self.label)

        main_menu = self.menuBar()
        file_menu = main_menu.addMenu("&File")
        file_menu.addAction(action1)

        self.show()

    def update_label(self):
        print("updating label")
        self.label.setText('Updated label')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    GUI = Window()
    sys.exit(app.exec_())

The app has one action which can be triggered by Ctrl-U. The app works as expected. Now I have written a unit test suite:

import unittest
from PyQt5.QtWidgets import QApplication
from PyQt5.Qt import QKeySequence
from PyQt5.Qt import Qt
from myapp import Window
from PyQt5.QtTest import QTest


########################################################################
class Test(unittest.TestCase):

    #----------------------------------------------------------------------
    def setUp(self):
        self.app = QApplication([])
        self.ui = Window()
        self.menu = self.ui.menuBar()

    #----------------------------------------------------------------------
    def test_update_label_from_menu(self):
        self.assertEqual(self.ui.label.text(), 'Empty')
        self.menu.actions()[0].menu().actions()[0].trigger()
        self.assertEqual(self.ui.label.text(), 'Updated label')

    #----------------------------------------------------------------------
    def test_update_label_from_shortcut(self):
        self.assertEqual(self.ui.label.text(), 'Empty')
        QTest.keyClicks(self.ui, 'U', Qt.ControlModifier)
        print(self.ui.label.text())

        QTest.keyPress(self.ui, 'U', Qt.ControlModifier)
        print(self.ui.label.text())

        QTest.keyPress(self.ui, Qt.Key_U, Qt.ControlModifier)
        print(self.ui.label.text())

        self.assertEqual(self.ui.label.text(), 'Updated label')
        return

    #----------------------------------------------------------------------
    def tearDown(self):
        pass

When the test test_update_label_from_menu is executed, the action is being triggered and I can see that the label text has been updated and the test passes. However, I am not able to send the shortcut properly using QTest in the test_update_label_from_shortcut test (I am trying various combinations). The test fails.

How do I do send the event such as pressing Ctrl-N using QTest?

UPDATE: It looks like I am already using the suggested way in this answer (which is calling QTest.keyClicks(self.window, 'n', Qt.ControlModifier)) and it does not work for me. Not sure if this matters that much, but the answer refers to PySide, yet I am on PyQt.

UPDATE 2: In order to be able to make the test pass, I had to add QTest.qWaitForWindowExposed(self.ui) line in the setUp. Then the key presses can be sent using shortcuts.

Alex Tereshenkov
  • 3,340
  • 8
  • 36
  • 61
  • @ekhumoro, thanks for posting the link. I've looked into it and it looks like I am already doing it in the way the answer describes but it seems as the shortcut of key presses does not have any effect. Do you think there is something wrong with my sample code I have published? – Alex Tereshenkov Feb 05 '18 at 21:16
  • I ported the script to pyqt5 and added it to the answer. It works fine for me, but I only tested on linux (qt 5.10.0, pyqt 5.10). As explained in the answer, it may be necessary to wait for the window to be fully shown in order for it to work properly. However, that approach may not work on all platforms (see the comments). Please can you try the pyqt5 version of the script shown in my answer, and confirm whether it works or not on your platform (there's no need to use your own test case, as it won't make any difference). – ekhumoro Feb 05 '18 at 22:25
  • @ekhumoro, thanks for the PyQt5 version. I got it working. The only row I had to add is `QTest.qWaitForWindowExposed(self.ui)`. Since I show my windows in a synchronous way, I didn't think I would have to make sure they are "exposed" first. Appreciate the help. – Alex Tereshenkov Feb 06 '18 at 08:06

0 Answers0