Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PyQt unit test that QDialog is created

Tags:

I have a parent widget that in some cases calls a custom QDialog to get user input. How do I write a unit test to ensure the dialog is called, and that it will handle correct input correctly?

Here's a mini example:

from PyQt5.QtWidgets import QDialog, QVBoxLayout, QWidget, QLabel, QApplication
from PyQt5.Qt import pyqtSignal, QPushButton, pyqtSlot, QLineEdit
import sys


class PopupDialog(QDialog):
    result = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        lbl = QLabel("That's not a full number! Try again?")
        layout.addWidget(lbl)
        self.field = QLineEdit(self)
        layout.addWidget(self.field)

        self.btn = QPushButton("Ok")
        self.btn.clicked.connect(self.on_clicked)
        layout.addWidget(self.btn)

    def on_clicked(self):
        value = self.field.text().strip()
        self.result.emit(value)
        self.close()


class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.init_UI()

    def init_UI(self):
        layout = QVBoxLayout()
        self.setLayout(layout)

        lbl = QLabel("Please provide a full number")
        layout.addWidget(lbl)

        self.counter_fld = QLineEdit(self)
        self.counter_fld.setText("1")
        layout.addWidget(self.counter_fld)

        self.btn = QPushButton("start")
        layout.addWidget(self.btn)
        self.btn.clicked.connect(self.on_clicked)

        self.field = QLabel()
        layout.addWidget(self.field)
        self.show()

    @pyqtSlot()
    def on_clicked(self):
        txt = self.counter_fld.text()
        self.dialog = None
        try:
            result = int(txt) * 100
            self.field.setText(str(result))
        except ValueError:
            self.dialog = PopupDialog()
            self.dialog.result.connect(self.catch_dialog_output)
            self.dialog.exec_()

    @pyqtSlot(str)
    def catch_dialog_output(self, value):
        self.counter_fld.setText(value)
        self.on_clicked()


def main():
    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

So in this case, I'd want to write a unit test that inserts different values into self.field and then tests that it works without PopupDialog for integers but that the PopupDialog is called when inserting a string.

(I know I could just test the functionality without the dialog, and that for this problem, the QDialog is not actually needed. I just tried to keep the example simple. Baseline is: I can get the unit test through the steps until the popup dialog is created, but how can I then test that it is indeed created, and then interact with it to test the result?)

#!/usr/bin/env Python3

import unittest
import temp2

class Test1(unittest.TestCase):
    @classmethod
    def setUpClass(self):
        self.w = temp2.Example()

    def testHappy(self):
        for i in [0,1,5]:
            self.w.counter_fld.setText(str(i))
            self.w.btn.click()
            value = self.w.field.text()
            self.assertEqual(value, str(i * 100))

    def testSad(self):
        for i in ["A", "foo"]:
            self.w.counter_fld.setText(str(i))
            self.w.btn.click()
            # now what?


if __name__ == "__main__":
    unittest.main()   

(I'm using PyQt5 in Python3.6 on Windows.)

like image 461
CodingCat Avatar asked Mar 08 '19 13:03

CodingCat


1 Answers

Well there are few ways to check if the QDialog is created,

1) patch the PopupDialog and verify if it was called.

from unittest.mock import patch

@patch("temp2.PopupDialog")
def testPopupDialog(self, mock_dialog):
        self.w.counter_fld.setText(str("A"))
        self.w.btn.click()
        mock_dialog.assert_called_once()

2) To interact with the PopupDialog you may have to do a bit more.

def testPopupDialogInteraction(self):
        self.w.counter_fld.setText(str("A"))
        self.w.btn.click()
        if hasattr(self.w.dialog, "field"):
            self.w.dialog.field.setText(str(1))
            self.w.dialog.btn.click()
            value = self.w.field.text()
            self.assertEqual(value, str(1 * 100))
        raise Exception("dialog not created")

On a different note, there is a better way to verify the user input i.e QRegExpValidator. Check this SO answer

In the present method, it will continue creating a Popup everytime a user inputs improper value and would create a poor user-experience(UX). Even websites use validators instead of Popups.

like image 53
Ja8zyjits Avatar answered Sep 28 '22 19:09

Ja8zyjits