Тест модального диалога с Qt Test

Я пытаюсь написать модульный тест для приложения с графическим интерфейсом, используя QTestLib. Проблема в том, что один из слотов создает модальный диалог, используя exec() и я не нашел возможности взаимодействовать с диалогом.

Слоты, которые создают диалог, связаны с QAction. Итак, первая проблема заключается в том, что тестовый блок блокируется, когда я запускаю QAction в тесте, поскольку это приводит к вызову exec(), Поэтому я попытался создать QThread, который выполняет взаимодействие. Однако это не помогло.

Вещи, которые я уже пробовал (все выполнено в потоке "помощник по взаимодействию"):

  1. Отправить ключевые нажатия с помощью QTest::keyClicks()
    • В результате появляется сообщение об ошибке "QCoreApplication::sendEvent(): Невозможно отправить события объектам, принадлежащим другому потоку"
  2. Опубликовать QKeyEvents используя QCoreApplication::postEvent()
    • Не работает, т.е. ничего не происходит. Я предполагаю, потому что события заканчиваются в цикле событий потока, которому принадлежит диалог, который не будет достигнут, пока диалог не закрыт и exec() возвращается. См. Изменить ниже
  3. Вызов слотов в диалоге с помощью 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 и модульное тестирование?

Другие вопросы по тегам