Наконец-то и мне потребовалось, да не просто потому что мейнстрим, а реально потребовались тесты, ибо говнокодить над проектом стал не только я один.

ВАЖНО:
В данном случае мне потребовалось протестировать как работаем весь проект в ЦЕЛОМ, а не отдельные классы. То есть, по сути, симулировать те или иные действия не только в классах, но и в интерфейсе (QML, но сейчас не про это).
Основная проблема: нужно подключение всего проекта целиком (всех файлов), а так же полностью main.cpp.

Если Вам не нужны такие извращения, то лучше прочитать: https://doc.qt.io/archives/qt-4.8/qtestlib-manual.html

Следуя мануалу Qt нужно создать новый проект Unit test, в нём класс для теста, с помощью мастера создания Qt сделает базовый скелет. Если я правильно понял…

Но вот тут начинаются проблемы. Следуя задумке Qt:

  1. Один проект — один класс для теста
  2. С помощью макроса QTEST_MAIN (или QTEST_APPLESS_MAIN) создаётся функция main.
  3. Каждый проект соответственно компилируется в бинарник
  4. Соответственно каждый тест запускается отдельно и не зависимо от других.

Мне кажется такая организация немного не логичной…
Т.е. если класс сильно зависит от других  (вообще это не айс, но всякое бывает), то придётся чуть ли не весь основной проект добавлять в инклуды и так каждый тест, ну и если класс использует QML, то тут точно в main нужно прописывать инициализацию qml, установку всех свойств и переменных и т.д.

Нужно, что бы в QtCreator работала панель «Tests», в которой удобно просматривать результаты тестирования и выборочно запускать сами тесты, вот так это выглядит:

Можно, конечно,  реализовать все классы тестов просто в  отдельной папке проекта (например Tests), убрать все макросы QTEST_MAIN, и запускать все тесты из функции main основного проекта, но тут минус в том, что они будут выполняться при любом запуске проекта, ну и выборочный запуск будет работать через жопу, либо вообще не работать — только все сразу. 

Вот так, например:

void main()
{
    ...
    MyTestClass1 tc1;
    if (QTest::qExec(&tc1, argc, argv)>0) return 42;
    
    MyTestClass2 tc2;
    if (QTest::qExec(&tc2, argc, argv)>0) return 42;
    ...
}

Ну или делать через подключаемые библиотеки и т.д. — кто на сколько головой ударился.

Кому достаточно такого поведения, можно дальше не париться =)

У меня как раз случай, что требуется инклудить весь проект, что бы протестировать какой-либо класс, т.к. идёт обширная работа с QML, а для этого нужно основное окно, вспомогательные классы и т.д.

Решение такое:

Просто основной файл проекта *.pro сделаем подключаемым для проекта с тестами.

Как именно:

Изменим текущий проект на проект с поддиректориями.
Для этого просто создаём поддиректорию в текущей папке и переносим всё в неё. Дальше в родительской папке создаём MyProjectTOTAL.pro файл, в него прописываем:

TEMPLATE = subdirs

SUBDIRS += \
    Tests \
    MyProject

Так же создаём подпроект «Проект автотестирования» с помощью визарда Qt Creator, у меня он назван «Tests».

Теперь в папке MyProject создаём текстовый файл с расширением «MyProject.pri» и переносим в него всё из «MyProject.pro».
Из SOURCES убираем main.cpp и переносим его обратно в *.pro

Потом в MyProject.pri в самом верху прописываем:

VPATH = $PWD #Что бы файлы подключались относительно оригинального расположения файла
INCLUDEPATH += $PWD

Ну и приинклудим myproject.pri в myproject.pro

include(myproject.pri)
SOURCES += main.cpp

Отдельный *.pri файл нужен, что бы его же подключить к проекту с тестами, но без main.cpp и не иметь проблем с расположением классов для инклуда.

Теперь осталось изменить немного функцию main, что бы когда она была в основном проекте, то запускалась бы как обычно, а когда была в тестовом проекте, запускала бы тест.

#include <QApplication>
#include <QQmlApplicationEngine>
......

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main/main.qml")));
    .....

   //-- Даём возможность инклудить нас в тесты
    #ifdef MY_TEST
        return runTest(argc, argv);
    #else
        return app.exec();
    #endif
}

С помощью проверки, объявлен ли макрос запускаем на исполнение тест, иначе всё как обычно.

Такой финт ушами нужен, потому что QTestLib сканирует классы на наличие  «QTest::qExec(….)» и, если у нас этого не будет в самом классе, то тест не будет доступен в панельке, поэтому объявление функции запуска будет находиться в самом тестовом классе, а запуск этой функции из main.

Так, теперь как использовать:

В *.pro файле тестового проекта прописываем:

include(../MyProject/MyProject.pri)

И в *.cpp тестовом классе подключаем нашу main функцию для запуска основного проекта и теста:

int runTest(int argc, char *argv[]) //-- Нужно, что бы парсер тестов нашёл этот тест, поэтому запускаем мы его из main
{
    MyTestClassName t;
    return QTest::qExec(&t, argc, argv);
}

#define MY_TEST
#include "../MyProject/main.cpp"

Таким образом в одном «тестовом» проекте может быть несколько классов с тестами и не нужно ничего добавлять в *.pro файл тестов — всё уже будет сразу, как только это добавляется в основной проект.

Надеюсь, принцип понятен. Только с относительными путями не наебитесь 😀

Вот теперь всё тестится, управляется, и да же желание тесты писать появилось =)

3 комментария

1 час нервов 30 мая 2021 - 14:50

VPATH = $PWD #Что бы файлы подключались относительно оригинального расположения файла
INCLUDEPATH += $PWD

Тут два знака $$ должно быть

Reply
Чувак 15 февраля 2019 - 3:22

Спасибо! Сначала проигнорировал, через месяц вспомнил про Ваш пост. Стоило дописать в каких ситуациях может понадобиться такое извращение. В моём случае мне нужно отловить баг, воспроизведя действия пользователя и проверять нашёл ли я его — вручную каждый раз запускать на тысячный раз надоело. Так же реализовал другие «сценарии» использования/поведения пользователя с программой вцелом, а не только проверку одного класса. Ещё раз спасибо!
P.S. Хотелось бы увидеть статью про логирование — что писать, куда писать, как писать.

Reply
Kemppi 12 января 2019 - 10:09

С прошедшими праздниками! Модульное тестирование, или юнит-тестирование ( англ. unit testing ) — процесс в программировании, позволяющий проверить на корректность отдельные модули исходного кода программы, наборы из одного или более программных модулей вместе с соответствующими управляющими данными, процедурами использования и обработки.

Reply

Leave a Comment

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

You may also like