Тест модального диалога с Qt Test
Я пытаюсь написать модульный тест для приложения с графическим интерфейсом, используя QTestLib. Проблема в том, что один из слотов создает модальный диалог, используя exec()
и я не нашел возможности взаимодействовать с диалогом.
Слоты, которые создают диалог, связаны с QAction. Итак, первая проблема заключается в том, что тестовый блок блокируется, когда я запускаю QAction в тесте, поскольку это приводит к вызову exec()
, Поэтому я попытался создать QThread, который выполняет взаимодействие. Однако это не помогло.
Вещи, которые я уже пробовал (все выполнено в потоке "помощник по взаимодействию"):
- Отправить ключевые нажатия с помощью
QTest::keyClicks()
- В результате появляется сообщение об ошибке "QCoreApplication::sendEvent(): Невозможно отправить события объектам, принадлежащим другому потоку"
- Опубликовать QKeyEvents используя
QCoreApplication::postEvent()
-
Не работает, т.е. ничего не происходит.Я предполагаю, потому что события заканчиваются в цикле событий потока, которому принадлежит диалог, который не будет достигнут, пока диалог не закрыт иexec()
возвращается.См. Изменить ниже
-
- Вызов слотов в диалоге с помощью
QMetaObject::invokeMethod()
-
Не работает, т.е. ничего не происходит.Я думаю, по той же причине, что иpostEvent()
не работаетСм. Изменить ниже
-
Итак, вопрос: есть ли способ программно взаимодействовать с модальным диалогом, который был открыт с помощью exec()
метод?
Изменить: На самом деле, метод 3 работает. Проблема была в другом: я передал аргументы invokeMethod()
в поток "помощника взаимодействия" и по какой-то причине доступ к аргументам не работал из этого потока (я не получил ошибок SEG, но они были просто пустыми). Я думаю, что метод 2 также работает, и у меня просто была та же проблема, что и с методом 3, но я не проверял это.
4 ответа
Решение, которое я использую в приложениях командной строки, которые используют библиотеки Qt, предназначенные для графических интерфейсов, - это singleShot
, как этот ответ намекает. В этих случаях это выглядит так:
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.
Вы можете сохранить взаимодействие в том же потоке, отложив его выполнение до начала цикла событий диалога.
Например, прямо перед exec()
звоните, вы используете либо QTimer::singleShot
с интервалом 0 или QMetaObject::invokeMethod
с типом подключения Qt::QueuedConnection
вызвать слот, который должен быть выполнен, пока отображается диалоговое окно.
Вы также можете опубликовать события перед звонком 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));
Ответ на связанный вопрос содержит некоторые дополнительные сведения об очистке очереди событий во время теста: Цикл событий Qt и модульное тестирование?