Разработка для 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

    Также, нужно указать файлы, которые будут тестироваться. Самый простой способ сделать это — создать *.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 или группе ВКонтакте.

    Автор: Максим Костерин
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое