Question

I have written a QAbstractListModel class to expose data from my library to QML. It seems to work correctly together with the QML ListView component, but I'd like to write some tests for it to ensure it continues to behave.

I've been testing other parts of my QML module via Qt Quick Test, but couldn't work out how to access the model directly from QML. I'm after simple things like checking the row count, and accessing role data values for arbitrary rows in the model.

Is this actually possible, or would I need to write the tests in C++?

Was it helpful?

Solution

This is a subject that I always have to look up myself, and I'm still a bit unsure, so there may be better ways than what I suggest. Anyway, it seems that QAbstractItemModel doesn't provide the functions that you're trying to test. I can think of two ways to get around that.

Add them yourself

#include <QtGui/QGuiApplication>
#include <QQmlContext>
#include <QQuickView>
#include <QDebug>
#include "qtquick2applicationviewer.h"

#include "QAbstractListModel"

class Model : public QAbstractListModel
{
    Q_OBJECT
public:
    Model() {}

    int rowCount(const QModelIndex &parent) const
    {
        return mList.size();
    }

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const{
        if (index.row() < 0 || index.row() >= mList.size()) {
            return QVariant();
        }
        return mList.at(index.row());
    }

    Q_INVOKABLE QVariant get(int index) {
        return data(createIndex(index, 0));
    }

    Q_INVOKABLE void append(QVariant element) {
        beginInsertRows(QModelIndex(), mList.size() + 1, mList.size() + 1);
        mList.append(element.toMap().value("name").toString());
        endInsertRows();
    }
private:
    QList<QString> mList;
};

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QtQuick2ApplicationViewer viewer;
    Model model;
    viewer.rootContext()->setContextProperty("model", &model);
    viewer.setMainQmlFile(QStringLiteral("qml/quick/main.qml"));
    viewer.showExpanded();

    return app.exec();
}

#include "main.moc"

Here's a small hack of a QML test case:

import QtQuick 2.2

Item {
    width: 360
    height: 360

    Component.onCompleted: {
        model.append({name: "blah"});
        console.assert(model.get(0) === "blah");
    }
}

Use QQmlListProperty

This is only useful if your model is or can be wrapped by a QObject subclass, as it is, as its name suggests, used as a property of an object. It's useful for it's convenience, but is less flexible than the first option, as it can only store QObject-derived object pointers. I won't go into details on this, as I'm assuming that this is not relevant for your use case.

You can find more information below:

Using C++ Models with Qt Quick Views

OTHER TIPS

I know this topic is old, but anyway here is your answer.

Once your QAbstractListModel is defined on cpp side, you access delegate's roles via delegate's property:

import QtQuick 2.5
import QtTest 1.0

Item {
    width: 1    // the minimum size of visible compoment
    height: 1   // the minimum size of visible compoment

    ListView {
        id: testView
        model: yourModelId
        delegate: Item {
            property variant dataModel: model
        }
    }

    TestCase {
        when: windowShown

        function test_dataModelReadDelegate() {
            testView.currentIndex = 0
            testView.currentItem.dataModel["yourRole"]);
        }
    }
}

Be careful, ListView must be visible (also TestCase waits until windowShown is true), otherwise the delegates will not be created (TestCase itself is invisible component).

And for some reason, when the root Item has no size, I'll get warning about invalid component size.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top