对于这个问题,我有一个解决方案,不需要对生产代码进行任何修改。此流程中的主要问题QMessageBox
是通常使用它自己的消息循环(按exec()
方法)调用。这意味着像这样的直接解决方案将不起作用:
p_button->show();
QTest::qWaitForWindowActive(p_btn);
QTest::mouseClick(p_button, Qt::LeftButton);//connected to msgbox.exec()
//next row will be not executed since we are still in event loop of msgbox
QTest::keyEvent(QTest::Click, qApp->activeWindow(), Qt::Key_Return);
所以你需要期待 QMessageBox在它出现之前。一种方法是创建 eventFilter 来查找 activate QMessageBox
。这样,QMessageBox
如果需要,您还可以验证属性。
想象一下你有这样的功能:
QAbstractButton* CreateWidget(QWidget* ip_parent)
{
auto p_btn = new QPushButton(ip_parent);
QObject::connect(p_btn, &QAbstractButton::pressed, []() {
QMessageBox msgBox;
msgBox.setText("Are you sure?");
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.exec();
});
return p_btn;
}
它将创建将在按下时执行QMessageBox
的按钮。要对其进行测试,您可以使用这样的助手:
class MessageWatcher : public QObject {
public:
using tDialogChecker = std::function<void(QMessageBox*)>;
MessageWatcher(tDialogChecker i_checker, QObject* ip_parent = nullptr)
: QObject(ip_parent)
, m_checker(i_checker)
{
qApp->installEventFilter(this);
}
bool eventFilter(QObject* ip_obj, QEvent* ip_event) override
{
if (auto p_dlg = qobject_cast<QMessageBox*>(ip_obj)) {
if (ip_event->type() == QEvent::WindowActivate) {
m_checker(p_dlg);
return true;
}
}
return false;
}
private:
tDialogChecker m_checker;
};
它会在接收到 for 类型QEvent::WindowActivate
的事件时调用 lambda QMessageBox
。您可以执行与自身相关的任何检查,QMessageBox
并且可以在QMessageBox
那里执行关闭。考虑这个简单的测试:
class WidgetLibTest : public QObject {
Q_OBJECT
private slots:
void WidgetLibCheck();
};
void WidgetLibTest::WidgetLibCheck()
{
MessageWatcher watcher([](auto ip_msg_box)
{
auto closer = qScopeGuard([ip_msg_box] { QTest::keyEvent(QTest::Click, ip_msg_box, Qt::Key_Return); });
QCOMPARE(ip_msg_box->text(), "Are you sure?");
});
auto p_btn = std::unique_ptr<QAbstractButton>(CreateWidget(nullptr));
p_btn->show();
QTest::qWaitForWindowActive(p_btn.get());
QTest::mouseClick(p_btn.get(), Qt::LeftButton);//will execute QMessageBox
}
qScopeGuard
需要,因为当QCOMPARE
失败时它会调用 return ,这将跳过其余的代码。所以,每次都要关闭 QMessageBox,即使检查不正确,也需要使用它。
以类似的方式,您还可以测试QProgressDialog
或任何将在您的纠缠实现中弹出的对话框。也可以测试小部件\对话框的级联,只有在这种情况下,您才需要某种函子数组。但我建议避免这种情况并重组实现,以便可以分别测试每个组件。