Unit Tests для проекта из одного класса

У меня есть проект Qt widgets и я хочу создать тесты для нескольких функций (MainWindow::RPN). Как это сделать через Qt Creator? Ну или как перенести проект на vs и сделать там? Я перепробовал все что смог нагуглить (и оф документацию, и с хабара).

#.pro
QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

DEFINES += QT_DEPRECATED_WARNINGS

SOURCES += \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui
RC_FILE = CalculatorIco.rc


CONFIG(testcase) {

    QT += testlib

    HEADERS += \
        testtest.h

    SOURCES += \
        test_main.cpp\
        testtest.cpp

}
else {

    SOURCES += \
        main.cpp
}

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

testtests.cpp

//testtests.cpp
#include "testtest.h"
#include "mainwindow.h"
#include <QtTest>

TestTests::TestTests(QObject *parent) : QObject(parent)
{

}

void TestTests::first_test()
{
    MainWindow w;
    QString tempString = "2 + 2.2";
    QVERIFY(w.RPN(tempString) == 4.2);
}

mainwindow.h

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    double RPN(QString inString);

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
/*...*/
double RPN(QString inQString) {
/*...*/
}

Тестовые файлы почти полностью копируют пример выше.


Ответы (1 шт):

Автор решения: Maxim Timakov

Qt Test можно использовать и в одиночном проекте, но для этого нужно настроить дополнительные конфигурации сборки.

В файле проекта настраиваем, что собирать и запускать(приложение или тесты) и какие исходники при этом используются.

У меня получился следующий файл проекта:

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

#
# Main Sources
#

SOURCES += \
    mainwindow.cxx

HEADERS += \
    mainwindow.hxx

FORMS += \
    mainwindow.ui


#
# Выбор исходников и параметров для сборки
#

CONFIG(testcase) {

#
# для сборки тестов
#

    QT += testlib

    HEADERS += \
        testtests.hxx

    SOURCES += \
        test_main.cxx\
        testtests.cxx

} else {

#
# для сборки приложения
#

    SOURCES += \
        main.cxx
}

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

То есть я создал отдельный main и добавил класс для тестов

Содержимое файлов:

//test_main.cxx
#include "testtests.hxx"

#include <QtTest>

QTEST_MAIN(TestTests)

//testtests.hxx
#ifndef TESTTESTS_HXX
#define TESTTESTS_HXX

#include <QObject>

class TestTests : public QObject
{
    Q_OBJECT
public:
    explicit TestTests(QObject *parent = nullptr);

private slots:
    void first_test();
};

#endif // TESTTESTS_HXX

//testtests.cxx
#include "testtests.hxx"

#include "mainwindow.hxx"

#include <QtTest>

TestTests::TestTests(QObject *parent) : QObject(parent)
{

}

void TestTests::first_test()
{
    MainWindow w;
    QVERIFY(true);
}

Тесты собираются при условии, что переменная CONFIG содержит testcase, иначе приложение

Теперь идем в конфигурацию проекта в Qt Creator и добавляем параметры сборки:

  • Жмем "добавить", выбираем тип и вводим название(можно скопировать и переименовать имеющиеся)
  • Правим "Основное - каталог сборки"
  • Разворачиваем "Сборка, этапы - qmake"
  • в поле "дополнительные параметры" вводим CONFIG+=testcase

qmake add unittest

Можно попробовать собрать и запустить тесты

qmake test success

Добавим тест для какой либо логики из MainWindow

void TestTests::second_test()
{
    MainWindow w;

    QString test{QString::fromUtf8("demo")};

    QCOMPARE(w.someMetod(test), test.size());
}

second test

Дополнение

то появляются две ошибки: undefined reference to `MainWindow::RPN(QString)' и error: ld returned 1 exit status.

У вас в заголовочном файле RPN объявлена как метод класса MainWindow

А в файле реализации(cpp) как простая функция

При вызове в тесте вы используете RPN как метод. Но тело метода не определено.

Решение: Так как RPN не зависит от приватных полей класса MainWindow, то можно сделать ее свободной функцией, что так же облегчит ее тестирование(не нужно создавать объект класса)

Для этого в файле mainwindow.h вынесите ее за пределы объявления класса:


double RPN(QString inString);

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

//    double RPN(QString inString);

В файле теста замените вызовы метода на вызов функции

void TestTests::first_test()
{
    // MainWindow w;
    QString tempString = "2 + 2.2";
    QVERIFY(/*  w. */RPN(tempString) == 4.2);
}

P.S.: для сравнений двух значений лучше использовать QCOMPARE

→ Ссылка