Developing for Sailfish OS: unit testing with Qt/C++ under OS Sailfish

Hello! This article is a continuation of a series of articles about testing Sailfish-applications (previous article), and this time we will look at unit testing C++ projects for Sailfish OS.

the

Test application


So we have a basic example app for Sailfish OS, which is available in repository of the project (on how to create an app for Sailfish OS, you can read one of previous articles). QML component contains a single screen with a greeting.

the
import QtQuick 2.0
import Sailfish.Silica 1.0

ApplicationWindow 
{
Label 
{ 
x: Theme.horizontalPageMargin 
text: "Hello Sailors" 
color: Theme.secondaryHighlightColor 
font.pixelSize: Theme.fontSizeExtraLarge 
}
}

In addition, there is one small class in C++:
the
class MyClass {
public:
MyClass();
MyClass(int first, int second);
int add() const;
int multiply() const;
private:
int firstValue, secondValue;
};

Its meaning is simple – it stores 2 values and finds their sum and product. This class we are going to test.

the

project Creation


Tests and the application itself must be placed as subdirectories of the same project. To do this, when creating a new project select "Project subdirectories":


Or in an already established *.pro file to specify the TEMPLATE = subdirs. This template indicates that the project contains a subdirectory. Now in the context menu of the main project have the option to add sub-projects:


Such subprojects need at least two: one of them will be the application itself, the second test to him. When you create with QtCreator, they will be automatically added to *.pro file as a subdirectory. If the app is already created, its name can simply be added to the variable SUBDIRS *.pro hand. In the end, SUBDIRS will look something like this:
the
SUBDIRS = \
app \ 
tests

Let's create the tests. For unit testing in Qt framework QtTest used, which was already mentioned in previous article. To use it, you need to *.yaml file project dependencies when building with Pkconfig add Qt5Test.
To create the test *.pro file of the subproject that contains a set of tests, you should connect the module testlib:
the
QT += testlib

Also, you need to specify the files that will be tested. The easiest way to do this is to create *.pri file in the subdirectory with the project and specify the path to the test class:
the
HEADERS += $$PWD/src/myclass.h 
SOURCES += $$PWD/src/myclass.cpp 

Further, it must be included in *.pro application files and the project with the tests:
the
INCLUDEPATH += ../app/
include(../app/app.pri)

In TARGET specifies the name of the subproject. Later, a file with the same name you will need to run to run the tests.

After that, *.pro file of the test subproject will look like the following:
the
TARGET = SailfishProjectTest

CONFIG += sailfishapp qt c++11

QT += testlib

HEADERS += testmyclass.h

SOURCES += testmyclass.cpp \
nain.cpp

INCLUDEPATH += ../app/
include(../app/app.pri)

the

Writing tests


For test-writing is implemented with a separate class containing test scripts. He should be the heir of the class QObject. The tests themselves are added as private slots in this class. Each of the slots will act as test functions.

It should be noted that in the QtTest library there are methods to customize the data for your tests before their implementation, as well as to clean up after tests are run:
the

    initTestCase() called before the first test function, if it fails, no test function will not be executed.

    cleanupTestCase() — is invoked after all the test functions.

    init() is called before each test function, if it fails, the subsequent test will fail.


Using the information described above, it is possible to about the class responsible for testing of our project:
the
#include <QObject>
#include "src/myclass.h"

class TestMyClass : public QObject {
Q_OBJECT
private:
MyClass myClass;
private slots:
void init();
void testAdd();
void testMultiply();
};

To compare the results of function execution with the expected result uses macro substitution:
the

    QVERIFY(condition) argument takes an expression and, if error, displays a standard error message in the test log.

    QVERIFY2(condition, message) similarly QVERIFY(), but prints the specified arguments to the message if the condition is false.

    QTRY_VERIFY_WITH_TIMEOUT(condition, timeout) is similar to QVERIFY(), but repeats the comparison until the condition will be true or until the time specified in the second argument.

    QTRY_VERIFY2_WITH_TIMEOUT(condition, message, timeout) is similar to QVERIFY2() repeats the comparison is the same as QTRY_VERIFY_WITH_TIMEOUT().

    QTRY_VERIFY(condition), QTRY_VERIFY2(condition, message) same as above, but with the timer in 5 seconds.

    QCOMPARE(actual, expected) gives more detailed information about the failed test. As arguments are passed to the function execution result and the expected result if they do not match, then both of these values are displayed in the log testing.

    QTRY_COMPARE_WITH_TIMEOUT(actual, expected, timeout) similarly QCOMPARE(), but repeats the comparison until the values will not be correct, or not yet reached the specified time in milliseconds.

    QTRY_COMPARE(actual, expected) same as QTRY_COMPARE_WITH_TIMEOUT(), but with the timer in 5 seconds.


More information about macros can be found in documentation QTest.

Use the information above to write our test function:
the
#include <QtTest/QtTest>
#include "src/myclass.h"
#include "testmyclass.h"

void TestMyClass::init() {
myClass = MyClass(4, 2);
}

void TestMyClass::testAdd() {
QCOMPARE(myClass.add(), 6);
}

void TestMyClass::testMultiply() {
QCOMPARE(myClass.multiply(), 8);
}

Since in our project the test class is divided into .h and .cpp files, in this step, the process of writing unit tests is completed. However, if .h file is missing, and the whole class is fully described in .cpp file, you must connect the automatically generated .moc file. For example, #include "testmyclass.moc".

The last thing that remains to be done is to arrange entry point for running tests. To do this, in *.cpp the class file with the tests, either in a separate main.cpp is one of three macros: QTEST_MAIN()/QTEST_APPLESS_MAIN()/QTEST_GUILESS_MAIN(). The argument they are given the name of the test class. Each macro declares a function main(), so it can be used only once in the subproject. Different classes with the unit tests should be placed in separate sub-projects.
the

Running tests


So, the project is ready, launch it from the environment. After the successful launch on the device in directory /usr/bin will be a file with the name specified in TARGET. Simply execute this file.
the
*********Start testing of TestMyClass *********
Config: Using QtTest library 5.2.2, Qt 5.2.2
PASS : TestMyClass::initTestCase()
PASS : TestMyClass::testAdd()
PASS : TestMyClass::testMultiply()
PASS : TestMyClass::cleanupTestCase()
Totals: 4 passed, 0 failed, 0 skipped
********Finish testing of TestMyClass *********

the

Conclusion


This article was reviewed by way of writing unit tests for testing applications for the platform Sailfish OS. As an example, was considered a simple application, 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: Maxim Kosterin
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

A Bunch Of MODx Revolution + LiveStreet

Monitoring PostgreSQL with Zabbix

PostgreSQL load testing using JMeter, Yandex.Tank and Overload