Developing for Sailfish OS: Testing QML components
Hello! This article is a continuation of the series of articles dedicated to developing for the mobile platform Sailfish OS. This time we will discuss how to organize testing QML components apps written for mobile devices. Consider all stages from writing code to run tests on a real device.
the
As an example, we consider a simple application-the-counter. It contains a field that displays the current counter value. If you click on the button "add", then the counter is incremented by one. In the draw menu there is an item to reset counter value to zero.
the
the
For writing tests use the framework QtTest, specifically QML object type TestCase. With it, you can tap the screen and check the expected values on the real line. It should be noted that at the time of writing, the Sailfish SDK uses Qt 5.2, so not all methods listed in the documentation.
In order to use the framework QtTest in the application, you must add *.yaml dependency file for the Assembly under PkgConfigBR:
the
In this example, the variable tests.files is written the address of the directory with tests in the project, and in tests.path — the way in which these tests will be installed on the device.
the
Test files must begin with the prefix tst_. Write tests in the language of QML, where the root element is an object of type TestCase, which are declared inside functions. The functions that begin with the prefix test_ are considered tests and will be launched by the framework. Create for example a simple test and place it in the file tst_counter.qml:
the
To test QML application components you want to use his main element, which is defined in the file qml/name of the project. It is important that you can apply only to the elements defined in files with names in CamelCase format. Therefore, it creates an auxiliary file with the right name format (qml/Projectname) to which is transferred all the contents of the qml/name of the project. And in order to make the application, as before started, source file is inserted into the element Projectname. In our case, the contents of the file counter-application.qml -- new file CounterApplication.qml. In the file counter-application.qml we are subject to the following:
the
Now we need to configure the TestCase to run tests after downloading the app. Consider the properties of this object:
the
In order to test the QML-components of our application, we need to put TestCase inside the object that describes the app. Previously, we allocated the object in a separate file and can use it in other files. We need to use the properties when and windowShown to run the tests only when the application window is displayed. Also set a name for the test Suite in the property name. For our tests it looks like this:
the
Now the object CounterApplication available in the tests and we can interact with it and display them.
Framework QtTest provides methods to interact with Qt components. Unfortunately, from Sailfish Silica components are not standard, so we need to write methods for working with them. To solve this problem we extend the class TestCase, which we will add methods to communicate with Sailfish components. We create a file SailfishTestCase.qml, in which the root element is TestCase. Inside this TestCase we add the methods that you want to use inside our tests. Later in the file with the tests we use instead of the object TestCase object SailfishTestCase and use the added methods.
First we need to find an element that is displayed. In QML to access the items, use the property id, but it is inaccessible from the outside. Therefore, for items that we need to look at the display, we set the value of the property objectName looking for items using it. For search, you can organize recursive descent in depth with checking the value of object properties for equality desired. The method has been implemented, which allows to find an element that has some property has a given value:
the
The parameters of this method are:
To find the element for objectName was implemented an additional method, as this type of search most popular:
the
A vivid example of the custom component Qt is drawn menu, which is widely used in Sailfish apps. Among the methods TestCase there is no such that would allow one to select an item from that menu, so useful were the following implementation of this behaviour:
the
The method openPullDownMenu(element) allows you to simulate pulling the menu as it would make the user first pressing the screen, then the pointer is down to open a menu and released. Parameter is an object that contains the the menu.
Also useful method is clickElement(element), allowing you to click on the specified item and wait a second completion of the action initiated by the depression.
Combining the above steps we create a method clickPullDownElement(parent, name), which allows you to open a menu that contains the item passed to the method parent, and click on the element from which the value of the property objectName equal to the value parameter name.
Using these methods we can write tests for our application. The first one will increment the counter twice and check that the value has increased. The second will increase the counter value and reset it, then check that it is equal to 0.
the
The app is not closed between test runs and does not clear the data. Responsible for presetting and clearing data before and after performing tests lies entirely with the developer. In TestCase there are two methods that are invoked before and after executing each test case: init(), cleanup(). These methods should be used to return the status of the application in the original. There are also methods of initTestCase() and cleanupTestCase() called once before all tests run, and after, respectively.
In our example, it is necessary to reset the counter after performing each test, we add the following method implementation cleanup():
the
After completion of each test button is pressed the "reset" from the pull menu.
the
Before you run tests, you must build and deploy the app on the device (both the physical device and the emulator). This process is described from our article cycle. In order to be able to run tests on the device, you must install two packages with the commands:
the
Thus we set on device QtTest framework that will allow us to run our tests.
To run tests we use qmltestrunner utility, which as the parameter pass the path to the files with the tests. It looks as follows:
the
As a result, we see the following:
the
In conclusion, in addition we added two tests test_counterAdd() and test_counterReset() displays the method calls initTestCase() and cleanupTestCase().
the
The result was considered a way of writing tests for QML-component in applications for the platform Sailfish OS. As an example, was considered a simple application-the counter, the source of which (together with tests) is available at GitHub.
Technical questions can be discussed on channel Russian-speaking community of Sailfish OS in Telegram or Facebook page.
Author: Sergey Averkiev
Article based on information from habrahabr.ru
the
Test application
As an example, we consider a simple application-the-counter. It contains a field that displays the current counter value. If you click on the button "add", then the counter is incremented by one. In the draw menu there is an item to reset counter value to zero.
![]() |
![]() |
the
application settings
For writing tests use the framework QtTest, specifically QML object type TestCase. With it, you can tap the screen and check the expected values on the real line. It should be noted that at the time of writing, the Sailfish SDK uses Qt 5.2, so not all methods listed in the documentation.
In order to use the framework QtTest in the application, you must add *.yaml dependency file for the Assembly under PkgConfigBR:
- Qt5Test
. Next, you need to specify the installation path for tests *.pro file as follows:the
tests.files = tests/*
tests.path = /usr/share/counter-application/tests
INSTALLS += tests
OTHER_FILES += tests/*
In this example, the variable tests.files is written the address of the directory with tests in the project, and in tests.path — the way in which these tests will be installed on the device.
the
Implementation tests
Test files must begin with the prefix tst_. Write tests in the language of QML, where the root element is an object of type TestCase, which are declared inside functions. The functions that begin with the prefix test_ are considered tests and will be launched by the framework. Create for example a simple test and place it in the file tst_counter.qml:
the
import QtQuick 2.0
import Sailfish.Silica 1.0
import QtTest 1.0
TestCase {
test_addition function() {
compare(2 + 2, 4);
}
}
To test QML application components you want to use his main element, which is defined in the file qml/name of the project. It is important that you can apply only to the elements defined in files with names in CamelCase format. Therefore, it creates an auxiliary file with the right name format (qml/Projectname) to which is transferred all the contents of the qml/name of the project. And in order to make the application, as before started, source file is inserted into the element Projectname. In our case, the contents of the file counter-application.qml -- new file CounterApplication.qml. In the file counter-application.qml we are subject to the following:
the
CounterApplication { }
Now we need to configure the TestCase to run tests after downloading the app. Consider the properties of this object:
the
- name: string — name of the test Suite to report
- when: bool — must be set to true, to run the test Suite (default true)
completed: bool — is set to true, after the completion of the test Suite the
optional: bool — if the flag is set true, then the test is skipped (the default false)
running: bool — contains the value true if the test Suite is run the
windowShown: bool — contains the value true, if the component containing the TestCase was displayed
In order to test the QML-components of our application, we need to put TestCase inside the object that describes the app. Previously, we allocated the object in a separate file and can use it in other files. We need to use the properties when and windowShown to run the tests only when the application window is displayed. Also set a name for the test Suite in the property name. For our tests it looks like this:
the
CounterApplication {
TestCase {
name: "Counter tests"
when: windowShown
test_addition function() {
compare(2 + 2, 4);
}
}
}
Now the object CounterApplication available in the tests and we can interact with it and display them.
Framework QtTest provides methods to interact with Qt components. Unfortunately, from Sailfish Silica components are not standard, so we need to write methods for working with them. To solve this problem we extend the class TestCase, which we will add methods to communicate with Sailfish components. We create a file SailfishTestCase.qml, in which the root element is TestCase. Inside this TestCase we add the methods that you want to use inside our tests. Later in the file with the tests we use instead of the object TestCase object SailfishTestCase and use the added methods.
First we need to find an element that is displayed. In QML to access the items, use the property id, but it is inaccessible from the outside. Therefore, for items that we need to look at the display, we set the value of the property objectName looking for items using it. For search, you can organize recursive descent in depth with checking the value of object properties for equality desired. The method has been implemented, which allows to find an element that has some property has a given value:
the
function findElementWithProperty(parent, propertyKey, propertyValue, exact, root) {
if (parent.visible) {
if (exact && parent[propertyKey] === propertyValue) return parent
if (!exact && parent[propertyKey] !== undefined &&
parent[propertyKey].search(propertyValue) !== -1) {
return parent
}
}
if (parent.children !== undefined && parent.visible) {
for (var i = 0; i < parent.children.length; i++) {
var element = findElementWithProperty(parent.children[i], propertyKey,
propertyValue, exact, false);
if (element !== undefined) return element;
}
}
if (root) {
fail("Element with key property '" + propertyKey + "' and value '" +
propertyValue + "' not found");
} else {
return undefined;
}
}
The parameters of this method are:
- root — true if the current item is the start
parent — the element from which to start search
propertyKey — the property whose value is checked
propertyValue — the property value that you want to find
exact — true if you want the full compliance of the required value is found, otherwise the value is searched as a substring the
To find the element for objectName was implemented an additional method, as this type of search most popular:
the
function findElementWithObjectName(root, name) {
return findElementWithProperty(root, "objectName", name, true, true);
}
A vivid example of the custom component Qt is drawn menu, which is widely used in Sailfish apps. Among the methods TestCase there is no such that would allow one to select an item from that menu, so useful were the following implementation of this behaviour:
the
function openPullDownMenu(element) {
var x = element.width / 2;
var startY = element.height / 10;
by(element, x, startY);
for (var i = 1; i <= 5; i++) {
mouseMove(element, x, startY * i);
}
mouseRelease(element, x, startY * i);
}
function clickElement(element) {
mouseClick(element, element.width / 2, element.height / 2);
wait(1000);
}
clickPullDownElement function(parent, name) {
openPullDownMenu(parent);
clickElement(findElementWithObjectName(parent, name));
}
The method openPullDownMenu(element) allows you to simulate pulling the menu as it would make the user first pressing the screen, then the pointer is down to open a menu and released. Parameter is an object that contains the the menu.
Also useful method is clickElement(element), allowing you to click on the specified item and wait a second completion of the action initiated by the depression.
Combining the above steps we create a method clickPullDownElement(parent, name), which allows you to open a menu that contains the item passed to the method parent, and click on the element from which the value of the property objectName equal to the value parameter name.
Using these methods we can write tests for our application. The first one will increment the counter twice and check that the value has increased. The second will increase the counter value and reset it, then check that it is equal to 0.
the
CounterApplication {
SailfishTestCase {
name: "Counter tests"
when: windowShown
test_counterAdd function() {
var button = findElementWithObjectName(pageStack.currentPage, "addButton");
clickElement(button);
clickElement(button);
compare(findElementWithObjectName(pageStack.currentPage, "countText").text "2");
}
test_counterReset function() {
var button = findElementWithObjectName(pageStack.currentPage, "addButton");
clickElement(button);
clickElement(button);
clickPullDownElement(pageStack.currentPage, "resetItem");
compare(findElementWithObjectName(pageStack.currentPage, "countText").text "0");
}
}
}
The app is not closed between test runs and does not clear the data. Responsible for presetting and clearing data before and after performing tests lies entirely with the developer. In TestCase there are two methods that are invoked before and after executing each test case: init(), cleanup(). These methods should be used to return the status of the application in the original. There are also methods of initTestCase() and cleanupTestCase() called once before all tests run, and after, respectively.
In our example, it is necessary to reset the counter after performing each test, we add the following method implementation cleanup():
the
CounterApplication {
SailfishTestCase {
name: "Counter tests"
when: windowShown
...
function cleanup() {
clickPullDownElement(pageStack.currentPage, "resetItem");
}
}
}
After completion of each test button is pressed the "reset" from the pull menu.
the
building and running tests
Before you run tests, you must build and deploy the app on the device (both the physical device and the emulator). This process is described from our article cycle. In order to be able to run tests on the device, you must install two packages with the commands:
the
pkcon install qt5-qtdeclarative-import-qttest
pkcon install qt5-qtdeclarative-devel-tools
Thus we set on device QtTest framework that will allow us to run our tests.
To run tests we use qmltestrunner utility, which as the parameter pass the path to the files with the tests. It looks as follows:
the
/usr/lib/qt5/bin/qmltestrunner -input /usr/share/counter-application/tests/
As a result, we see the following:
the
********* Start testing of qmltestrunner *********
Config: Using QtTest library 5.2.2, Qt 5.2.2
PASS : qmltestrunner::Counter tests::initTestCase()
PASS : qmltestrunner::Counter tests::test_counterAdd()
PASS : qmltestrunner::Counter tests::test_counterReset()
PASS : qmltestrunner::Counter tests::cleanupTestCase()
Totals: 4 passed, 0 failed, 0 skipped
********* Finished testing of qmltestrunner *********
In conclusion, in addition we added two tests test_counterAdd() and test_counterReset() displays the method calls initTestCase() and cleanupTestCase().
the
Conclusion
The result was considered a way of writing tests for QML-component in applications for the platform Sailfish OS. As an example, was considered a simple application-the counter, the source of which (together with tests) is available at GitHub.
Technical questions can be discussed on channel Russian-speaking community of Sailfish OS in Telegram or Facebook page.
Author: Sergey Averkiev
Комментарии
Отправить комментарий