1

У меня есть проект 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) {
/*...*/
}

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

  • Qt Test Для тестов вам потребуется отдельный проект(если используется qmake) либо отдельная цель сборки(при использовании cmake) – Maxim Timakov Aug 20 '20 at 18:12
  • Я пробовал и так, но встает проблема доступа к файлам основного проекта, даже на полностью указанный адрес выдает ошибку "No such file or directory". А так же moc file not found – Sen 11111 Aug 20 '20 at 18:25
  • Какую систему сборки вы используете? qmake, cmake или что то другое? – Maxim Timakov Aug 20 '20 at 18:28
  • 1
    Я использую qmake. – Sen 11111 Aug 20 '20 at 18:47
  • Добавил в pro тестового проекта подключения из pro основного и удалил moc файл. Скомпилировалось, но теперь ругается на test_1::test_1() { MainWindow a; int b = a.func(2); QVERIFY(b == 4); } возможно как-то по другому надо проверять? Я нашёл только такой пример. Ошибки: undefined reference to `MainWindow::MainWindow(QWidget*)', и на остальные функции основного проекта. – Sen 11111 Aug 20 '20 at 19:03
  • @Sen11111 https://ru.stackoverflow.com/q/536546/398802 – dIm0n Aug 20 '20 at 19:26
  • @Sen11111, если тестируемые функции не зависят от UI, то лучше вынести их в отдельный файл. Это позволит протестировать их независимо от класса окна. – Maxim Timakov Aug 21 '20 at 18:59
  • Если я правильно понял нужно исправить в mainwindow.cpp double RPN(QString inQString) на double MainWindow::RPN(QString inQString), это тоже не помогло, ошибка undefined reference to `RPN(QString)' – Sen 11111 Aug 21 '20 at 19:42

1 Answers1

2

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(&quot;demo&quot;)};

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

  • В таком виде у меня все работает, да. Но если изменить содержимое first_test() на MainWindow w; QString tempString = "2 + 2.2"; QVERIFY(w.RPN(tempString) == 4.2); то появляются две ошибки: undefined reference to `MainWindow::RPN(QString)' и error: ld returned 1 exit status. В самой IDE файлы mainwindow.h и mainwindow.cpp подключены, на include "mainwindow.h" тоже не ругается, однако все равно не воспринимает ссылку. – Sen 11111 Aug 21 '20 at 18:33
  • @Sen11111, undefined reference означает, что вы что то недоподключили в файле проекта. – Maxim Timakov Aug 21 '20 at 18:51
  • 1
    @Sen11111, Обратите внимание, что перед методом класса в файле реализации указывается имя класса. – Maxim Timakov Aug 21 '20 at 19:28
  • Спасибо, заработало. Последний раз выдавало undefined reference в другом методе, в котором при написании мне пришлось оставить double RPN(QString inString); дабы оно работало, а после изменения функции на метод, эта строка выдавала ошибку. – Sen 11111 Aug 21 '20 at 20:02