Qt and QML testing tools

QML

qml_tests launch all the tests related to the interface. The daemon and libclient SHOULD be trusted in this part; hence, connectivity test scenarios are not required. Ideally, mock data should be used to avoid depending on network events. This may be difficult sometimes, and some tools may be missed because tests in this part are a work in progress. Here are some tools and principles to enable tests to be written quickly.

Mock data

Say a UI test for the AccountComboBox depending on a list of accounts is required. Instead of creating real accounts, a mock list of accounts should be created. The easy way to do this is to serialize/unserialize a real AccountComboBox model. First, the serialized model is required:

diff --git a/src/app/mainview/components/AccountComboBoxPopup.qml b/src/app/mainview/components/AccountComboBoxPopup.qml
index 2f1b633a..0df2594d 100644
--- a/src/app/mainview/components/AccountComboBoxPopup.qml
+++ b/src/app/mainview/components/AccountComboBoxPopup.qml
@@ -43,6 +43,15 @@ Popup {
         color: JamiTheme.transparentColor
     }

+    function saveModel() {
+        var modelData = []
+        for (var i = 0; i < listView.model.count; ++i) {
+            modelData.push(listView.model.get(i))
+        }
+        var json = JSON.stringify(modelData)
+        console.warn(json)
+    }
+
     focus: true
     closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside

@@ -241,6 +250,7 @@ Popup {
                 width: root.width
                 onClicked: {
                     root.close();
+                    root.saveModel();
                     LRCInstance.currentAccountId = ID;
                 }
             }

saveModel() will print the serialized structure whenever the combo box is clicked. Here is the result:

[{"Alias":"AmarOk","ID":"a2d724d18a943e6c","NotificationCount":0,"Status":5,"Type":1,"Username":"amarok"},{"Alias":"Munnin","ID":"8a22b7d0176327db","NotificationCount":0,"Status":5,"Type":1,"Username":"munnin"},{"Alias":"TEST JAMI","ID":"3b7d2b9e2af83a47","NotificationCount":0,"Status":5,"Type":2,"Username":"696"},{"Alias":"Sébastien Blin","ID":"131ad59045a9a146","NotificationCount":0,"Status":5,"Type":1,"Username":"sblin"}]

Now, this can be easily used in a test. The best way is to add this data to a variable or to a separate js file (cf. https://doc.qt.io/qt-6/qtqml-documents-networktransparency.html). An example of using the data in a test:

TestWrapper {
    AccountComboBoxPopup {
        id: uut

        function loadMockData() {
            return JSON.parse('[\
                {"Alias":"Foo","ID":"a2d724d18a943e6c","NotificationCount":0,"Status":5,"Type":1,"Username":"foo"}, \
                {"Alias":"Bar","ID":"8a22b7d0176327db","NotificationCount":0,"Status":5,"Type":1,"Username":"bar"}, \
                {"Alias":"TEST JAMI","ID":"3b7d2b9e2af83a47","NotificationCount":0,"Status":5,"Type":2,"Username":"696"}, \
                {"Alias":"Whatever","ID":"131ad59045a9a146","NotificationCount":0,"Status":5,"Type":1,"Username":"whatever"}]')
        }

        TestCase {
            name: "Check model size"
            function test_checkModelSize() {
                var accountList = findChild(uut, "accountList")
                accountList.model = uut.loadMockData()
                compare(accountList.model.length, 4)
            }
        }
    }
}

C++

GoogleTest

GoogleTest is Google’s C++ testing and mocking framework.

Installation

  • Ubuntu / Debian: apt install googletest libgtest-dev

Example main.cpp

#include <gtest/gtest.h>

TEST(Test, Test1)
{
    EXPECT_EQ(0, 0); // OK
    EXPECT_EQ(1, 0); // ERROR and continues
    ASSERT_EQ(0, 0); // OK
    ASSERT_EQ(1, 0); // ERROR and stops execution
}

int main(int argc, char *argv[])
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();  // Runs Test1 and any other included test
}

QML

QtQuickTest

Installation

  • Ubuntu / Debian: apt install qml-module-qqtest libqt5quicktest5

  • Example main.cpp

#include <QtQuickTest/quicktest.h>
#include <QQmlEngine>

class Setup : public QObject
{
    Q_OBJECT

public:
    Setup() {}

public slots:

    void qmlEngineAvailable(QQmlEngine *engine)
    {
        // Code to be run before the tests
    }
};

QUICK_TEST_MAIN_WITH_SETUP(testqml, Setup)
#include "main.moc"