简体   繁体   中英

How to mock a QML component

Actually I'm trying to run some test on a QML component which embeds C++ objects . Unfortunately, I'm getting some errors when I execute my tests. The C++ objects aren't recognized by the QML file. That makes also sense as the C++ objects are set in the main.cpp file.

My question is: How can I mock an context property for performing QML tests? Or other said, how can I do unit-test with mixing Qt/QML code?

As I understand you right, you got same problem as I. Some time ago I wrote this mock: https://bitbucket.org/troyane/qml-cpp-template (you can use that code free for your purposes).

Take a look at main.cpp , there you can see two ways of doing things:

// 1 case:
// Register type and create object at QML side
qmlRegisterType<CppClass>("CppClassModule", 1, 0, "CppClass");
QQmlApplicationEngine engine(QUrl("qrc:///qml/main.qml"));
qDebug() << "Qt version: " << qVersion();
// 2 case:
// Create object here and "move it up" to QML side
// engine.rootContext()->setContextProperty("cppClass", new CppClass);

Good luck!

I got QML tests working for me without compiling in any C++ code.

In my case, I have a C++ object controller with a property called left_motor , which is another object, and that has a property speed .

Note that speed is readable, but not writable. Any updates will happen through slots. In QML that looks like this: controller.left_motor.onGuiSpeedChanged(speed)

I was able to mock this in QML using Item components, properties, and some javascript.

 Item { // mock of controller id: controller property alias left_motor: left_motor Item { id: left_motor property int speed: 0 function onGuiSpeedChanged(arg) { speed = arg } } } property alias controller: controller 

Now calls to controller.left_motor.onGuiSpeedChanged(speed) resolve like before, but connect into the mock function. I can even read back the speed property to know that the call happened.

Here is my test function (the code I'm testing is part of page1):

 function test_set_speed() { console.log("controller.left_motor.speed: " + controller.left_motor.speed) var got = page1.set_left_speed(250) compare(got, 250, "set_left_speed() return incorrect") console.log("controller.left_motor.speed: " + controller.left_motor.speed) } 

Note that it's important to use slots instead of writable properties. The call to a slot looks just like a function call and can be mocked as such. I could not figure out a way to mock out a property write.

I had started out trying writable properties because that was the first thing in the documentation on binding C++ and QML. It connects QML and C++ as expected, but can't be mocked out for testing.

The best solution would be to move your C++ types to a QML plugin so that the QML tests can import them. This plugin needs to be available to your tests at runtime, which means ensuring that they can be found in the import path . If you run into any issues with the plugin not being found, you can set the QML_IMPORT_TRACE environment variable to 1 to give you some useful debugging output.

However, even QML-only test cases involve some C++ to set them up .

To execute C++ code before any of the QML tests are run, the QUICK_TEST_MAIN_WITH_SETUP macro can be used:

// src_qmltest_qquicktest.cpp
#include <QtQuickTest>
#include <QQmlEngine>
#include <QQmlContext>

class Setup : public QObject
{
    Q_OBJECT

public:
    Setup() {}

public slots:
    void qmlEngineAvailable(QQmlEngine *engine)
    {
        engine->rootContext()->setContextProperty("myContextProperty", QVariant(true));
    }
};

QUICK_TEST_MAIN_WITH_SETUP(mytest, Setup)

#include "src_qmltest_qquicktest.moc"

The most flexible and powerful option is to write tests in C++ and load the QML from there .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM