Разработка для Sailfish OS: Модульное тестирование Qt/C++ под Sailfish OS

от автора

Здравствуйте! Эта статья является продолжением цикла статей о тестировании Sailfish-приложений (предыдущая статья), и на этот раз мы рассмотрим модульное тестирование С++ в рамках проектов для Sailfish OS.

Тестируемое приложение

Итак, у нас имеется элементарный пример приложения под 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

Там же указывается директория, в которой будут размещены тесты на устройстве:

test.files = *.h *.cpp test.path = /usr/share/SailfishProject/test INSTALLS += test 

Переменная test.files содержит путь и название добавляемых файлов, а test.path — их расположение на устройстве.
Кроме этого, нужно указать файлы, которые будут тестироваться. Самый простой способ сделать это — создать *.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)  test.files = *.h *.cpp test.path = /usr/share/SailfishProject/test INSTALLS += test 

Написание тестов

Для написания тестов реализуется отдельный класс, содержащий тестовые сценарии. Он должен быть наследником класса 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".

Последнее, что остается сделать, организовать точку входа для запуска тестов. Для этого реализуется отдельный метод main() в тестовом подпроекте. В нем необходимо создать экземпляр каждого класса, содержащего набор тестов. Для их выполнения используется функция qExec() из пространства имен QTest. В качестве аргументов она принимает указатель на экземпляр тестового класса и необязательный набор аргументов.

#include <QtTest/QtTest> #include "testmyclass.h"  int main() {     TestMyClass testMyClass;     QTest::qExec(&testMyClass); } 

Запуск тестов

Итак, проект готов, запускаем его из среды. После успешного запуска на устройстве в директории /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 или группе ВКонтакте.

Автор: Максим Костерин
ссылка на оригинал статьи https://habrahabr.ru/post/323518/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *