Здравствуйте! Эта статья является продолжением цикла статей о тестировании Sailfish-приложений (предыдущая статья), и на этот раз мы рассмотрим модульное тестирование С++ в рамках проектов для Sailfish OS.
Итак, у нас имеется элементарный пример приложения под Sailfish OS, который доступен в репозитории данного проекта (о том, как создать приложение для Sailfish OS можно прочитать в одной из предыдущих статей). QML составляющая содержит единственный экран с приветствием.
Кроме этого имеется один небольшой класс на языке C++:
Смысл его прост – он хранит 2 значения и находит их сумму и произведение. Этот класс мы и будем тестировать.
Тесты и само приложение необходимо разместить в качестве поддиректорий одного проекта. Для этого можно при создании нового проекта выбрать пункт «Проект с поддиректориями»:
Либо в уже созданном *.pro файле указать TEMPLATE = subdirs. Этот шаблон указывает, что проект содержит в себе поддиректории. Теперь в контекстном меню основного проекта имеется возможность добавить подпроекты:
Таких подпроектов нужно хотя бы два: один их них будет являться самим приложением, второй — тестами к нему. При создании с помощью QtCreator, они автоматически добавится в *.pro файл в качестве поддиректории. Если приложение уже создано, его название можно просто добавить в переменную SUBDIRS в *.pro вручную. В итоге SUBDIRS будет выглядеть примерно так:
Перейдем к созданию самих тестов. Для модульного тестирования в Qt применяется фреймворк QtTest, который уже упоминался в предыдущей статье. Чтобы его использовать, нужно в *.yaml файле проекта в зависимости при сборке с помощью PkgConfig добавить Qt5Test.
Для создания тестов, в *.pro файле подпроекта, содержащего набор тестов следует подключить модуль testlib:
Также, нужно указать файлы, которые будут тестироваться. Самый простой способ сделать это — создать *.pri файл в поддиректории с проектом, и в нем указать путь к тестируемым классам:
Далее его нужно включить в *.pro файлы приложения и проекта с тестами:
В TARGET указывается название подпроекта. Позже, файл с таким же именем необходимо будет запустить для выполнения тестов.
После этого, *.pro файл тестового подпроекта будет выглядеть примерно следующим образом:
Для написания тестов реализуется отдельный класс, содержащий тестовые сценарии. Он должен быть наследником класса QObject. Сами тесты добавляются в виде приватных слотов этого класса. Каждый из слотов будет выступать в качестве тестовой функции.
Необходимо отметить, что в библиотеке QtTest присутствуют методы, позволяющие выполнить настройку данных для тестов перед их выполнением, а также прибраться после выполнения тестов:
Применив описанную выше информацию, можно получить примерно такой класс, отвечающий за тестирования нашего проекта:
Для сравнения результатов выполнения функции с ожидаемым результатом используются макроподстановки:
Больше информации о макросах можно найти в документации по QTest.
Воспользуемся информацией выше для написания наших тестовых функций:
Так как в нашем проекте тестирующий класс разделен на .h и .cpp файлы, то на этом шаге процесс написание модульных тестов завершается. Однако, если .h файл отсутствует, а весь класс полностью описан в .cpp файле, то нужно подключить автоматически генерируемый .moc файл. Например,
Последнее, что остается сделать, организовать точку входа для запуска тестов. Для этого в *.cpp файле класса с тестами, либо в отдельном main.cpp используется один из трех макросов: QTEST_MAIN()/QTEST_APPLESS_MAIN()/QTEST_GUILESS_MAIN(). В качестве аргумента им передается название тестового класса. Каждый из макросов объявляет функцию main(), поэтому может быть использовано только один раз в подпроекте. Разные классов с юнит-тестами следует располагать в отдельных подпроектах.
Итак, проект готов, запускаем его из среды. После успешного запуска на устройстве в директории /usr/bin появится файл с название, указанным в TARGET. Просто выполняем данный файл.
В данной статье был рассмотрен способ написания модульных тестов для тестирования приложений для платформы Sailfish OS. В качестве примера было рассмотрено простое приложение, исходники которого (вместе с тестами) доступны на GitHub.
Технические вопросы можно также обсудить на канале русскоязычного сообщества Sailfish OS в Telegram или группе ВКонтакте.
Автор: Максим Костерин
Тестируемое приложение
Итак, у нас имеется элементарный пример приложения под Sailfish OS, который доступен в репозитории данного проекта (о том, как создать приложение для Sailfish OS можно прочитать в одной из предыдущих статей). QML составляющая содержит единственный экран с приветствием.
import QtQuick 2.0
import Sailfish.Silica 1.0
ApplicationWindow
{
Label
{
x: Theme.horizontalPageMargin
text: "Hello Sailors"
color: Theme.secondaryHighlightColor
font.pixelSize: Theme.fontSizeExtraLarge
}
}
Кроме этого имеется один небольшой класс на языке C++:
class MyClass {
public:
MyClass();
MyClass(int first, int second);
int add() const;
int multiply() const;
private:
int firstValue, secondValue;
};
Смысл его прост – он хранит 2 значения и находит их сумму и произведение. Этот класс мы и будем тестировать.
Создание проекта
Тесты и само приложение необходимо разместить в качестве поддиректорий одного проекта. Для этого можно при создании нового проекта выбрать пункт «Проект с поддиректориями»:
Либо в уже созданном *.pro файле указать TEMPLATE = subdirs. Этот шаблон указывает, что проект содержит в себе поддиректории. Теперь в контекстном меню основного проекта имеется возможность добавить подпроекты:
Таких подпроектов нужно хотя бы два: один их них будет являться самим приложением, второй — тестами к нему. При создании с помощью QtCreator, они автоматически добавится в *.pro файл в качестве поддиректории. Если приложение уже создано, его название можно просто добавить в переменную SUBDIRS в *.pro вручную. В итоге SUBDIRS будет выглядеть примерно так:
SUBDIRS = app \
tests
Перейдем к созданию самих тестов. Для модульного тестирования в Qt применяется фреймворк QtTest, который уже упоминался в предыдущей статье. Чтобы его использовать, нужно в *.yaml файле проекта в зависимости при сборке с помощью PkgConfig добавить Qt5Test.
Для создания тестов, в *.pro файле подпроекта, содержащего набор тестов следует подключить модуль testlib:
QT += testlib
Также, нужно указать файлы, которые будут тестироваться. Самый простой способ сделать это — создать *.pri файл в поддиректории с проектом, и в нем указать путь к тестируемым классам:
HEADERS += $$PWD/src/myclass.h
SOURCES += $$PWD/src/myclass.cpp
Далее его нужно включить в *.pro файлы приложения и проекта с тестами:
INCLUDEPATH += ../app/
include(../app/app.pri)
В TARGET указывается название подпроекта. Позже, файл с таким же именем необходимо будет запустить для выполнения тестов.
После этого, *.pro файл тестового подпроекта будет выглядеть примерно следующим образом:
TARGET = SailfishProjectTest
CONFIG += sailfishapp qt c++11
QT += testlib
HEADERS += testmyclass.h
SOURCES += testmyclass.cpp nain.cpp
INCLUDEPATH += ../app/
include(../app/app.pri)
Написание тестов
Для написания тестов реализуется отдельный класс, содержащий тестовые сценарии. Он должен быть наследником класса QObject. Сами тесты добавляются в виде приватных слотов этого класса. Каждый из слотов будет выступать в качестве тестовой функции.
Необходимо отметить, что в библиотеке QtTest присутствуют методы, позволяющие выполнить настройку данных для тестов перед их выполнением, а также прибраться после выполнения тестов:
- initTestCase() — вызывается перед первой тестовой функцией, если в нем возникнет ошибка, ни одна тестовая функция не будет выполнена.
- cleanupTestCase() — вызывается после выполнения всех тестовых функций.
- init() — вызывается перед каждой тестовой функцией, если в нем возникнет ошибка, последующий тест не будет выполнен.
- cleanup() — вызывается после каждой тестовой функции.
Применив описанную выше информацию, можно получить примерно такой класс, отвечающий за тестирования нашего проекта:
#include <QObject>
#include "src/myclass.h"
class TestMyClass : public QObject {
Q_OBJECT
private:
MyClass myClass;
private slots:
void init();
void testAdd();
void testMultiply();
};
Для сравнения результатов выполнения функции с ожидаемым результатом используются макроподстановки:
- QVERIFY(condition) в качестве аргумента принимает выражение и, в случае его ошибочности, выводит стандартное сообщение об ошибке в журнал тестирования.
- QVERIFY2(condition, message) аналогично QVERIFY(), но выводит указанное в аргументах сообщение, если условие ложно.
- QTRY_VERIFY_WITH_TIMEOUT(condition, timeout) схожа с QVERIFY(), но повторяет сравнение, пока условие не окажется верным или пока не истечет время, указанное во втором аргументе.
- QTRY_VERIFY2_WITH_TIMEOUT(condition, message, timeout) схожа с QVERIFY2(), повторяет сравнение так же, как и QTRY_VERIFY_WITH_TIMEOUT().
- QTRY_VERIFY(condition), QTRY_VERIFY2(condition, message) аналогичны описанным выше, но с таймером в 5 секунд.
- QCOMPARE(actual, expected) дает более подробную информацию о провалившемся тесте. В качестве аргументов передаются результат выполнения функции и ожидаемый результат, если они не совпадают, то оба этих значения отображаются в журнале тестирование.
- QTRY_COMPARE_WITH_TIMEOUT(actual, expected, timeout) аналогично QCOMPARE(), но повторяет сравнение, пока значения не окажется верным, или пока не достигнуто указанное время в миллисекундах.
- QTRY_COMPARE(actual, expected) то же, что и QTRY_COMPARE_WITH_TIMEOUT(), но с таймером в 5 секунд.
Больше информации о макросах можно найти в документации по QTest.
Воспользуемся информацией выше для написания наших тестовых функций:
#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);
}
Так как в нашем проекте тестирующий класс разделен на .h и .cpp файлы, то на этом шаге процесс написание модульных тестов завершается. Однако, если .h файл отсутствует, а весь класс полностью описан в .cpp файле, то нужно подключить автоматически генерируемый .moc файл. Например,
#include "testmyclass.moc"
.Последнее, что остается сделать, организовать точку входа для запуска тестов. Для этого в *.cpp файле класса с тестами, либо в отдельном main.cpp используется один из трех макросов: QTEST_MAIN()/QTEST_APPLESS_MAIN()/QTEST_GUILESS_MAIN(). В качестве аргумента им передается название тестового класса. Каждый из макросов объявляет функцию main(), поэтому может быть использовано только один раз в подпроекте. Разные классов с юнит-тестами следует располагать в отдельных подпроектах.
Запуск тестов
Итак, проект готов, запускаем его из среды. После успешного запуска на устройстве в директории /usr/bin появится файл с название, указанным в TARGET. Просто выполняем данный файл.
*********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 *********
Заключение
В данной статье был рассмотрен способ написания модульных тестов для тестирования приложений для платформы Sailfish OS. В качестве примера было рассмотрено простое приложение, исходники которого (вместе с тестами) доступны на GitHub.
Технические вопросы можно также обсудить на канале русскоязычного сообщества Sailfish OS в Telegram или группе ВКонтакте.
Автор: Максим Костерин
Поделиться с друзьями