Тестирование с помощью модуля Qtestlib Qt
Я начал писать некоторые тесты с системой модульного тестирования Qt.
как вы обычно организуете тесты? Это один тестовый класс на один класс модуля или вы тестируете весь модуль с помощью одного тестового класса? Документы Qt предлагают следовать прежней стратегии.
Я хочу написать тесты для модуля. Модуль содержит только один класс, который будет использоваться пользователем модуля, но есть много логики, забранной в другие классы, которые я также хотел бы проверить, помимо тестирования общественного класса.
проблема в том, что предлагаемый Qt способ запуска тестов включает QTEST_MAIN
макро:
QTEST_MAIN(TestClass)
#include "test_class.moc"
и в конечном итоге одна тестовая программа способна тестировать только один тестовый класс. И это своего рода отстой для создания тестовых проектов для каждого отдельного класса в модуле.
конечно, можно было бы взглянуть на QTEST_MAIN
макрос, перепишите его и запустите другие тестовые классы. Но есть что-то, что работает из коробки?
так куда я это делаю вручную:
#include "one.h"
#include "two.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
TestOne one;
QTest::qExec(&one, argc, argv);
TestOne two;
QTest::qExec(&two, argc, argv);
}
4 ответов
да, QTest заставляет немного странную тестовую структуру и, как правило, уступает Google Test/Mock Framework. Для одного проекта я вынужден использовать QTest (требование клиента), и вот как я его использую:
- я компилирую все тесты вместе как проект шаблона subdir
- чтобы упростить создание новых тестов, я разделяю много конфигурации проекта, используя common.pri-файл я включаю в каждый тест .pro файл
- Если возможно, я разделяю каталог объектных файлов на ускорить компиляцию
- я запускаю их все с помощью пакетного сценария awk+sed.
настройка этих четырех точек Очень проста и делает использование QTest почти приятным. У вас есть некоторые проблемы с запуском нескольких тестов, которые не решаются в конфигурации, описанной выше?
PS: запуск тестов так, как вы это делаете, т. е. вызов нескольких QTest::qExec вызывает проблемы с переключателем командной строки-o - вы получите только результаты для последнего тестируемого класса.
связано с ответом, опубликованным @cjhuitt
это пример, который устраняет необходимость ручного вызова каждого тестового объекта
Я СТАРАЮСЬ ИЗБЕГАТЬ ТАКИХ ВЕЩЕЙ:
MyTestClass1 t1; t1.run();
MyTestClass2 t2; t2.run();
//etc...
мое решение-пусть тест-объекты наследуют от базового класса, который добавляет себя в статический список Затем основная программа выполняет все тестовые объекты в этом списке. Таким образом, ни один из поддерживающих рамочных кодов не нуждается в изменении. Единственное, что меняется, - это сами тестовые классы.
вот как я делаю это:
qtestsuite.h - базовый класс для тест-объекты
#ifndef QTESTSUITE_H
#define QTESTSUITE_H
#include <QObject>
#include <vector>
class QTestSuite : public QObject
{
Q_OBJECT
public:
static std::vector<QObject*> m_suites;
public:
explicit QTestSuite();
};
#endif // QTESTSUITE_H
qtestsuite.cpp
#include "qtestsuite.h"
#include <iostream>
std::vector<QObject*> QTestSuite::m_suites;
QTestSuite::QTestSuite() : QObject()
{
m_suites.push_back(this);
}
testall.cpp - выполняет тесты
#include "qtestsuite.h"
#include <QtTest/QtTest>
#include <iostream>
int main(int, char**)
{
int failedSuitesCount = 0;
std::vector<QObject*>::iterator iSuite;
for (iSuite = QTestSuite::m_suites.begin(); iSuite != QTestSuite::m_suites.end(); iSuite++)
{
int result = QTest::qExec(*iSuite);
if (result != 0)
{
failedSuitesCount++;
}
}
return failedSuitesCount;
}
mytestsuite1.cpp - пример тестового объекта, создайте больше из них
#include "qtestsuite.h"
#include <QtTest/QtTest>
class MyTestSuite1: public QTestSuite
{
Q_OBJECT
private slots:
void aTestFunction();
void anotherTestFunction();
};
void MyTestSuite1::aTestFunction()
{
QString str = "Hello";
QVERIFY(str.toUpper() == "this will fail");
}
void MyTestSuite1::anotherTestFunction()
{
QString str = "Goodbye";
QVERIFY(str.toUpper() == "GOODBYE");
}
static MyTestSuite1 instance; //This is where this particular test is instantiated, and thus added to the static list of test suites
#include "mytestsuite1.moc"
также, чтобы создать .pro файл
qmake -project "CONFIG += qtestlib"
в нашей настройке с QTest мы сделали несколько вещей, чтобы сделать его лучше.
- определите подкласс QObject, который используется в качестве базового класса для любого нового класса модульного теста.
- в конструкторе для этого класса мы добавляем экземпляр теста в статический список тестов, а в деструкторе удаляем его.
- затем у нас есть статическая функция, которая перебирает тесты и запускает их с помощью
QTest::qExec()
. (Мы накапливаем значения, возвращаемые каждый раз, и верните это из нашей функции.) -
main()
вызывает эту функцию и возвращает результат как success/failure. - наконец, в блок компиляции самого конкретного теста мы обычно включаем статический экземпляр этого класса.
Эта настройка означает, что класс будет создан до main()
выполняется, поэтому он будет добавлен в список классов для тестирования при запуске main. Фреймворк требует, чтобы вам просто нужно было наследовать свой класс правильно и создайте экземпляр статического экземпляра, если вы всегда хотите его запустить.
мы тоже иногда создать другие дополнительные тесты, которые добавляются на основе параметров командной строки.
обычно я организовываю тесты с одним исполняемым тестом для каждого тестируемого класса.
и в итоге одна тестовая программа способный испытывать как раз один тест класс.
Это хорошо. Он изолирует ваши тесты друг от друга, предотвращая такие вещи, как сбой в одном тесте, от блокировки всех других тестов. Этот сбой может быть вызван общим компонентом в нескольких тестируемых классах. Шаблон неудач затем подсказал бы вам коренное происхождение проблемы. В принципе, у вас есть лучшая диагностическая информация для сбоев, если ваши тесты независимы друг от друга.
сделайте его легким настроить множественные исполняемые файлы и побежать каждый тест отдельно. Используйте Test runner для создания всех тестовых процессов.
обновление:
Я немного передумал. Как только у вас есть большая программа с большим количеством тестов, связывание сотен тестовых исполняемых файлов становится очень медленным. Моя новая предпочтительно поместить все тесты библиотеки в исполняемый файл и выбрать, какие тесты вызывать с помощью аргументов командной строки, переданных исполняемому файлу.
это сокращает количество исполняемых файлов с сотен до десятков, но сохраняет преимущества запуска тестов отдельно.