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.)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With