Testing a modal dialog with Qt Test

I am trying to write unit test for a GUI application using QTestLib. The problem is that one of the slots creates a modal dialog using exec() , and I did not find the ability to interact with the dialog.

The slots that create the dialogue are associated with a QAction. So the first problem is that the test blocks when I run QAction in the test, as this leads to the call to exec() . So I tried to create a QThread that does the interaction. However, this did not help.

Things I've already tried (they all run from the helper helper thread):

  • Sending key clicks with QTest::keyClicks()
    • The results in the error message "QCoreApplication :: sendEvent (): cannot send events to objects belonging to another thread"
  • Publish QKeyEvents using QCoreApplication::postEvent()
    • Doesn't work, i.e. Nothing happens. I think because the events end in the event loop of the thread that owns the dialog that will not be reached until the dialog is closed and exec() returns. See "Editing below"
  • Calling slots in a dialog using QMetaObject::invokeMethod()
    • Doesn't work, i.e. Nothing happens. I think for the same reason that postEvent() does not work. See "Editing" below

So the question is: is there a way to interact programmatically with the modal dialog that was opened using the exec() method?

Edit: Actually, method 3. The problem was different: I passed the invokeMethod() arguments to the "helper interaction" stream, and for some reason the access to the arguments did not work from this stream (I did not have SEG errors, but they were just empty). I assume that method 2 also works, and I had the same problem as with method 3, but I did not test this.

+4
source share
4 answers

The solution I use in command line applications that use the Qt libraries designed for GUIs is singleShot , like this answer . In such cases, it looks like this:

 QCoreApplication app(argc, argv); // ... QTimer::singleShot(0, &app, SLOT(quit())); return app.exec(); 

So, in your case, I assume it will look something like this:

 QDialog * p_modalDialog = getThePointer(); // you will have to replace this with // a real way of getting the pointer QTimer::singleShot(0, p_modalDialog, SLOT(accept())); p_modalDialog->exec(); // called somewhere else in your case // but it will be automatically accepted. 
+5
source

You can save the interaction in one thread, delaying its execution until a cycle of dialog events starts.

For example, before calling exec() you use either QTimer::singleShot with 0 as the interval, or QMetaObject::invokeMethod with the connection type Qt::QueuedConnection to call the slot that needs to be executed while the dialog is displayed.

+2
source

You can also post events before calling exec() . As soon as the dialog is created after calling exec() , the events will be executed.

For example, to test the keystroke Esc (means reject / close the dialog box):

 // create a dialog QDialog d = ... //post an Escape key press and release event QApplication::postEvent(&d, new QKeyEvent(QEvent::KeyPress , Qt::Key_Escape, Qt::NoModifier) ); QApplication::postEvent(&d, new QKeyEvent(QEvent::KeyRelease, Qt::Key_Escape, Qt::NoModifier) ); // execute and check result int ret = d.exec(); QCOMPARE(ret, static_cast<int>(QDialog::Rejected)); 
+1
source

The related answer to the question contains some additional information on how to clear the event queue during the test: Qt event loop and unit testing?

0
source

Source: https://habr.com/ru/post/1399201/


All Articles