Search code examples
qtsystem-testing

Testing Qt application with Qt Test


I have looked at the 5 Qt testing examples including the one about GUI events, but these examples are way too simple.

I want to test my program by launching it, simulating some clicks, and checking the value of instance variables that have been changed by those clicks.

I assume that this test below is illogical: a.exec() blocks the thread until the program is closed, and when the program is closed w has been deleted I think (or will be deleted later?).

So how to write system/GUI tests?

My test:

void LaunchProgramTest::LaunchProgramTestFunction() {
    QApplication a(argc, argv);
    MainWindow *w = new MainWindow();

    w->show();

    a.exec();

    int testResult = w->myTestFunction();

    qDebug() << testResult; //Prints big numbers like "-17891602" or "1753770528" as if testResult was not initialized

    QVERIFY2(testResult == 3, "Incorrectly changed");
}

In mainWindow.h I declared a variable:

int testValue;

Mainwindow.cpp is the class for the main GUI of the program. In the constructor I added

testValue = 2;

Then in a function that is executed upon events I wrote

void MainWindow::on_actionTest_clicked() {
    testValue = 3;
}

Solution

  • enter code hereSo)) you need to add QTest, add .pro

    QT  += testlib
    

    and

    #include <QTest>
    

    I will show an example of my implementation for MousePress, the rest you can do yourself))

    struct EventMK
        {
            int type;
            QString m_widPath;
            int _timer;
            int width;
            int height;
            QPoint p;
            QPoint g;
            int button;
            int buttons;
            int modifiers;
            int _key;
            QString text;
            void print(){
                qDebug()<<"{ \n"
                   <<"type "<< type<<","<<"\n"
                   <<"Widget_Path "<< m_widPath<<","<<"\n"
                   <<"Timer "<< _timer<<","<<"\n"
                   <<"Width "<< width<<","<<"\n"
                   <<"Height "<< height<<","<<"\n"
                   <<"Pos_x "<< p.x()<<","<<"\n"
                   <<"Pos_y "<< p.y()<<","<<"\n"
                   <<"Global_x "<< g.x()<<","<<"\n"
                   <<"Global_y "<< g.y()<<","<<"\n"
                   <<"Button "<< button<<","<<"\n"
                   <<"Buttons "<< buttons<<","<<"\n"
                   <<"Modifiers "<< modifiers<<","<<"\n"
                   <<"Key "<< _key<<","<<"\n"
                   <<"Text "<< text<<"\n"
                   <<"}\n";
    
            }
        };
    
    QWidget * _getWidget(EventMK ev)
    {
        QString wname = ev.m_widPath;
        QStringList wpath = wname.split ( "/" );
        return QWidgetUtils::getAWidget(&wpath);
    }
    
    void _postExecution(EventMK ev, QWidget *widget)
    {
        if (widget){
    
            //set focus
            QWidgetUtils::setFocusOnWidget(widget);
    
            //end simulation
            widget->setUpdatesEnabled ( true );
            widget->update();
        }
    }
    
    
    QPoint adaptedPosition(EventMK ev, QWidget *w)
    {
        if (w == nullptr)
            return QPoint(ev.p.x(), ev.p.y());
    
        int orig_w = ev.width;
        int orig_h = ev.height;
        int curr_w = w->width();
        int curr_h = w->height();
    
        int new_x = ev.p.x() * curr_w / orig_w;
        int new_y = ev.p.y() * curr_h / orig_h;
    
        return QPoint(new_x, new_y);
    }
    

    and function implementation

    void executeMousePressEvent(EventMK ev)
    {
        QWidget* widget = _getWidget(ev);
        if ( widget == nullptr )
        {
            qDebug()<<"error: "<<__LINE__<<__FUNCTION__;
            return;  
        }
    
       // _preExecutionWithMouseMove(ev, widget);
        if (widget){
            QTest::mousePress ( widget, (Qt::MouseButton)ev.button , 
                                        (Qt::KeyboardModifier) ev.modifiers, 
                                         adaptedPosition(ev,widget));
    
        }
    
        _postExecution(ev, widget);
    }
    

    now left to fill struct EventMK , you need to populate it from MouseButtonPress events. Here is my example

     bool eventFilter(QObject *obj, QEvent *event)
        {
            ///
            /// process control
            ///
    
            //window events
            if (event->type() == QEvent::KeyPress)
            {
                handleKeyPressEvent(obj, event);
            }
            //mouse events
            else if (event->type() == QEvent::MouseButtonPress)
            {
                handleMousePressEvent(obj, event);
            }
            else if (event->type() == QEvent::MouseButtonRelease)
            {
                handleMouseReleaseEvent(obj, event);
            }
            else if (event->type() == QEvent::MouseButtonDblClick)
            {
                handleMouseDoubleEvent(obj, event);
            }
            else if (event->type() == QEvent::Wheel)
            {
                handleWheelEvent(obj, event);
            }
            //keyboard events
            else if (event->type() == QEvent::Close)
            {
                handleCloseEvent(obj, event);
            }
    
            ///the event should continue to reach its goal...
            return false;
        }
    

    and

    void handleMousePressEvent(QObject *obj, QEvent *event)
      {
            QWidget *widget = isValidWidget(obj);
            if (!widget){
                return;
            }        
            QMouseEvent *me = dynamic_cast< QMouseEvent*> ( event );
            //create the event
            if (widget != nullptr){
                EventMK evkm;
    
                evkm.type = QOE_MOUSE_PRESS;    // set your type
                evkm._timer = _timer.restart(); // add your QElapsedTimer
                evkm.m_widPath = QWidgetUtils::getWidgetPath(widget);
                evkm. width  = widget->width();
                evkm. height = widget->height();
    
                QPoint p ( me->pos() );
                QPoint g = widget->mapToGlobal ( p );
                evkm. p  = p;
                evkm. g = g;
    
                evkm. button  = me->button();
                evkm. buttons = me->buttons();
                evkm. modifiers = me->modifiers();
    
                evkm.print();
            }
            //send event if EventMK is valid
    
        }
    

    so, it turns out we can write a scenario and run what you wanted, thanks