Simulating Mouse and Touch Events.

Simulating Mouse and Touch events is likely one of the most useful tools that is offered by QML TestCase, and allows for more complex tests.

There are a couple of big gotchas for both Mouse and Touch Input that are not immediately obvious:

  1. In order to simulate either mouse or touch your test case MUST be inside of a component that has a defined size. If your root component does not have a defined size it will not be able to accept mouse or touch input.
  2. If you start a Press event (touch.press, or mousePress) ensure that it gets released by the end of your testFunction, or in your cleanup. Otherwise you will get angry error messages
  3. You must wait until your window is shown to start your tests.

Touch Events

Touch events start by defining a TouchEvent on a component. Once a touchEvent has been initialized it can be passed a touch action such as press, move, or release. After each event commit() must be called for the action to be passed to the component.

    let touch = touchEvent(component);
        touch.press(0,component);
        touch.commit();
        touch.release(0);
        touch.commit();

    

Touch Events are also able to be chained, and as long as each action is followed by a commit() you can create complex flow. By default a touch action will take place in the center of the component that a Touch Event is tied to. This can be controlled with optional arguements that define the x and y position of the touch.

Multitouch

Touch Events also support multitouch emulation. The first arguement in any touch action is the ID of the touch that you want to manipulate.

            let touch = touchEvent(component);
                touch.press(0,component);
                touch.press(1,component,1,1);//press(ID,component,x,y);
                touch.press(3,component,10,40);
                touch.commit();
                //test behavior on three touches
                touch.release(0);
                touch.release(1);
                touch.release(2);
                touch.commit();
        
            
        

Mouse Events

Unlike Touch Events, Mouse Events do not require defining anything. A mouse event can be affect any QML component. The simpiliest possible Mouse Event is a mouseClick

    
        mouseClick(component);
    

By default this will click on the component at its center, with the left mouse button. It has additional optional arguements to control x,y, button, modifies, and delays. In addition to mouseClick there is also mousePress, mouseRelease, mouseDrag, and mouseMove, and a couple others. I am going to focus mainly on mouseClick to keep things simple, with the rest following a similar enough pattern.

Example Tests:

These two test sets show implementation of a touch test and a mouse test that test the pressed state, as well as a press and drag off of a touch and mouse.

Component Code:

import QtQuick 2.0
    import QtTest 1.2
Rectangle{
    width:800
    height:800
    Background{
        id:bg
        anchors.fill: parent
    }
    TestCase{
        id:backgroundTests
        anchors.fill: parent
        name:"TestBackground"
        when:windowShown

        property color expectedBG:"green"
        function test_touched_background(){
            let clickRep = findChild(bg,"clickRep");
            let mouseArea = findChild(bg,"clickable")
            let touch = touchEvent(mouseArea);
            touch.press(0,mouseArea)
            touch.commit();
            compare(clickRep.color,backgroundTests.expectedBG);
            touch.release(0);
            touch.commit();
        }
    }
    TestCase{
        id:backgroundDragTest
        when:windowShown
    property color expectedBG:"yellow"
    function test_touch_release(){
        let clickRep = findChild(bg,"clickRep");
        let mouseArea = findChild(bg,"clickable")
        let touch = touchEvent(mouseArea);
        touch.press(0,mouseArea)
        touch.commit();
        touch.release(0);
        touch.commit();
        compare(clickRep.color,backgroundDragTest.expectedBG);
    }
    }
}

Note: It could be better to organize this into seperate test files for each test case.It mostly depends on personal prefernce in my opinion.

Mouse Test Code


    import QtQuick 2.0
    import QtTest 1.2
Rectangle{
    width:800
    height:800
    Background{
        id:bg
        anchors.fill: parent
    }
    TestCase{
        id:backgroundTests
        anchors.fill: parent
        name:"TestBackground"
        when:windowShown

        property color expectedBG:"yellow"
        function test_clicked_background(){
            let clickRep = findChild(bg,"clickRep");
            let mouseArea = findChild(bg,"clickable")
            mouseClick(mouseArea)
            compare(clickRep.color,backgroundTests.expectedBG);
        }
    }
    TestCase{
        id:backgroundDragTest
        when:windowShown
    property color expectedBG:"green"
    function test_mouse_press(){
        let clickRep = findChild(bg,"clickRep");
        let mouseArea = findChild(bg,"clickable")
        mousePress(mouseArea);
        compare(clickRep.color,backgroundDragTest.expectedBG);
        mouseRelease(mouseArea);
    }
    }
}


Additional Notes: Signal Spy

In additional to simply directly comparing component properties, or properties of children it is also possible to tie into and listen for signals that your component emits based on actions. For example if you have a component that emits a clicked signal based on its internal state it would be possible to use a Signal Spy to listen for that signal as part of your tests.
    
    Rectangle{
            width:800;
            height:800;
        //Component that emits a signal
        CustomComponent{
            id:custom
            anchors.fill:parent;
            //signal customClicked();
        }
        SignalSpy{
            id:spyder
            target:custom
            signalName:"customClick"
        }
        TestCase{
            name:"testing";
            function test_signal(){
                compare(syder.count,0)
                let touch = touchEvent(custom);
                touch.press(0,custom);
                touch.commit();
                compare(spyder.count,1);

            }
        }
    }