使用 Qt Test 测试模式对话框

Posted

技术标签:

【中文标题】使用 Qt Test 测试模式对话框【英文标题】:Test modal dialog with Qt Test 【发布时间】:2012-03-01 15:01:14 【问题描述】:

我正在尝试使用 QTestLib 为 GUI 应用程序编写单元测试。问题是其中一个插槽使用exec() 创建了一个模态对话框,我发现无法与该对话框进行交互。

创建对话框的槽连接到一个 QAction。所以第一个问题是当我在测试中触发 QAction 时测试会阻塞,因为这会导致调用exec()。因此,我尝试创建一个执行交互的 QThread。然而,这并没有帮助。

我已经尝试过的事情(都在“交互助手”线程中执行):

    使用QTest::keyClicks()发送按键点击 导致错误消息“QCoreApplication::sendEvent(): 无法将事件发送到不同线程拥有的对象” 使用QCoreApplication::postEvent() 发布 QKeyEvents 不起作用,即没有任何反应。我猜是因为事件最终出现在拥有对话框的线程的事件循环中,直到对话框关闭并且exec() 返回时才会到达。 请参阅下面的编辑 使用QMetaObject::invokeMethod() 在对话框中调用插槽 不起作用,即没有任何反应。我想与 postEvent() 不起作用的原因相同。 请参阅下面的编辑

所以问题是:有没有办法以编程方式与使用exec() 方法打开的模式对话框进行交互?

编辑:实际上,方法 3 有效。问题是另一个问题: 我将invokeMethod() 的参数传递给“交互助手”线程,由于某种原因,从该线程访问参数不起作用(我没有收到 SEG 错误,但它们只是空的)。 我猜方法 2 也有效,我只是遇到了与方法 3 相同的问题,但我没有测试。

【问题讨论】:

为了防止 cmets:我知道我也可以使用 setModal(true)show() 创建对话框并异步处理返回值。但我的问题是明确针对exec() 【参考方案1】:

我在使用用于 GUI 的 Qt 库的命令行应用程序中使用的解决方案是 singleShot,正如 this answer 所暗示的那样。在这些情况下,它看起来像这样:

QCoreApplication app(argc, argv);

// ...

QTimer::singleShot(0, &app, SLOT(quit()));
return app.exec();

所以在你的情况下,我想它看起来像这样:

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.

【讨论】:

我不知道为什么,但在我的测试中,我需要将 1 作为第一个参数传递给QTimer::singleShot,否则永远不会调用插槽(并且对话框会出现并耐心等待,直到我单击确定)。这就是 Mac OSX 10.6 上的“QTest library 4.8.1, Qt 4.8.1”。【参考方案2】:

您可以通过延迟执行直到对话框事件循环开始来保持交互在同一个线程中。

例如,在exec() 调用之前,您可以使用QTimer::singleShot 0 作为间隔或QMetaObject::invokeMethod 连接类型Qt::QueuedConnection 来调用需要在显示对话框时执行的槽。

【讨论】:

这个答案也是正确的。但是,我接受了另一个答案,因为它更详细。【参考方案3】:

您也可以在致电exec() 之前发布活动。 在exec() 调用后构建对话框后,将立即执行事件。

例如测试 Esc 按键(表示拒绝/关闭对话框):

// 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));

【讨论】:

【参考方案4】:

相关问题的答案有一些关于在测试期间刷新事件队列的额外细节: Qt event loop and unit testing?

【讨论】:

以上是关于使用 Qt Test 测试模式对话框的主要内容,如果未能解决你的问题,请参考以下文章

如何禁用 Qt Mainwindow 快捷方式适用于无模式对话框

Qt 之模式非模式半模式对话框

QT Qdialog

使用QT 4.7 - 对话框正忙着加载一件大事;如何禁用所有控件?

如何在 pytest-qt 中处理模态对话框而不模拟对话框

QT模态对话框及非模态对话框