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

И так, имеем проект с подпроектами. Хотим тесты. 

Следуя мануалу 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) подключаемым/шаблонным (*.pri) что бы его можно было инклудить в тестовые проекты,
для этого в *.pro  файле убираем из SOURCES  main.cpp (её мы будем подключать в тестовые проекты отдельно) и всё из *.pro файла переносим в *.pri файл (сначала создав обычный пустой текстовый файл),
потом в *.pri файле в самом верху прописываем:

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

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

include(common.pri)  #Подключаем основной класс проекта
SOURCES += main.cpp \  #Как обычно добавим в исходники наш основной main

Ну и теперь осталось изменить немного функцию 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.

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

В основном проекте создаёте проект с поддиректориями, называете, например «Tests», в этом подпроекте с помощью мастера создания создаёте уже проект «Unit test», например «test1».

В *.pro файле (у нас «test1.pro») тестового проекта прописываем:

include(../../MyProject/common.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"

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

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