Search code examples
c++qtqtest

Click on button for several QMessageBox in Qtest


I'm creating a test for my GUI application. At a certain point of the test, I want to click on a button which asks for user's confirmation and then, confirmation for each file I have to delete. So, on the test, to press that button I'm doing:

QTest::mouseClick(m_widget->removeButton, Qt::LeftButton);

But now, for the first QMessageBox I get I'm able to click on yes with:

QKeyEvent *evr = new QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier);
QApplication::postEvent(&m_widget->reply, evr);

From here, I have to ask for confirmation for each file I want to delete, but I'm not able to execute anything else until I click by me myself with the mouse or with any solution I'm trying to find. I observed with the qDebug that it won't go further of that mouseClick function until all those QMessageBox are clicked (it could be one or more).

All the QMessageBox are local variable on the app, nothing is static.


Solution

  • We resolved a similar issue by adding a layer of abstraction over the message boxes. We have a global object with functions to 'display' message boxes and dialogs as follows:

    struct QtFuncs
    {
        typedef std::function<int(QMessageBox*)> MessageBoxExec;
        MessageBoxExec messageBoxExec = [](QMessageBox* mb) { return mb->exec(); };
        // more functions for dialogs and standard message boxes (open file, ...)
    };
    struct QtGlobalFuncs
    {
        static QtFuncs& instance()
        {
            static auto fn = QtFuncs();
            return fn;
        }
    
        static int messageBoxExec(QMessageBox* box)
        {
            return instance().messageBoxExec(box);
        }
    };
    

    And when we want/need to execute a message box we would 'exec' it via:

    QMessageBox box(QMessageBox::Critical, "hi", "do you want to greet bob?", QMessageBox::Yes | QMessageBox::No);
    auto button = QtGlobalFuncs::messageBoxExec(&box);
    

    Note that this method requires you to replace all QMessageBox::exec calls with QtGlobalFuncs::messageBoxExec. In our test scenario we would then overwrite the internal function:

    int nTimesExecCalled = 0;
    QtGlobalFuncs::instance().messageBoxExec = [&nTimesExecCalled](auto box)
    {
        int res = QMessageBox::Yes;
        if (nTimesExecCalled)
            res = QMessageBox::No;
    
        ++nTimesExecCalled;
        return res;
    };
    
    QMessageBox box(QMessageBox::Critical, "hi", "do you want to greet bob?", QMessageBox::Yes | QMessageBox::No);
    auto button = QtGlobalFuncs::messageBoxExec(&box);
    

    I hope this small example helps you understand how we resolved this problem for us and maybe it will help you as well.

    Have a nice day :)