2009-10-06 2 views
43

Я только начинаю новый проект, который нуждается в кросс-платформенном графическом интерфейсе, и мы выбрали Qt как графический интерфейс.Какую единицу тестирования я должен использовать для Qt?

Нам также нужна модульная система тестирования. Еще около года назад мы использовали встроенную платформу для тестирования модулей для C++ - проектов, но теперь мы переходим к использованию Google Test для новых проектов.

Есть ли у кого-нибудь опыт использования Google Test для Qt-приложений? Является ли QtTest/QTestLib лучшей альтернативой?

Я все еще не уверен, сколько мы хотим использовать Qt в частях, не относящихся к GUI проекта, - мы, вероятно, предпочли бы просто использовать STL/Boost в основном коде с небольшим интерфейсом к Qt-based GUI.

EDIT: Похоже, что многие склоняются к QtTest. Есть ли кто-нибудь, кто имеет опыт интеграции с непрерывным сервером интеграции? Кроме того, мне показалось, что необходимость обработки отдельного приложения для каждого нового тестового примера вызовет много трений. Есть ли хороший способ решить это? У Qt Creator есть хороший способ справиться с такими тестовыми примерами или вам нужен проект на тест?

ответ

17

Я не знаю, что QTestLib «лучше», чем один каркас для другого в таких общих терминах. Есть одна вещь, в которой все хорошо, и это хороший способ тестирования приложений на основе Qt.

Вы можете интегрировать QTest в новую настройку на основе Google Test. Я не пробовал, но, основываясь на том, как QTestLib архивирован, похоже, что это будет не слишком сложно.

В тестах, написанных с использованием чистого QTestLib, есть опция -xml, которую вы можете использовать вместе с некоторыми преобразованиями XSLT для преобразования в необходимый формат для сервера непрерывной интеграции. Однако многое зависит от того, с каким сервером CI вы работаете. Я бы предположил, что это относится и к GTest.

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

Я ничего не знаю в Qt Creator, который потребует отдельного проекта на каждый тестовый пример, но он может измениться с тех пор, как я последний раз смотрел на Qt Creator.

Я также предлагаю придерживаться QtCore и держаться подальше от STL. Использование QtCore повсюду будет иметь дело с битами GUI, которые требуют типов данных Qt.В этом случае вам не придется беспокоиться о переходе от одного типа данных к другому.

+0

Я хотел бы указать на потомство, которое многое изменилось за 8+ лет с момента публикации исходного вопроса. В 2017 году выбор googletest/mock над QTestLib представляется гораздо более жизнеспособным выбором, чем раньше. Как приложение A, вот вебинар от ICS, озаглавленный [Qt Test-Driven Development с помощью Google Test и Google Mock] (https://www.ics.com/webinars/qt-tdd-using-google-test-and-google- фиктивный). Для меня использование googletest/mock вместе с 'QSignalSpy' было действительно эффективным, я не вижу причин возвращаться назад. – evadeflow

7

Почему бы не использовать модульный модуль, включенный в Qt? Пример: QtTestLib Tutorial.

+0

«Является ли QtTest/QTestLib лучшей альтернативой?» ... Я думаю, что это вопрос: -P – Terrabits

3

QtTest в основном полезен для тестирования деталей, для которых требуется цикл Qt event/signal dispatching. Он разработан таким образом, что для каждого тестового примера требуется отдельный исполняемый файл, поэтому он не должен противоречить какой-либо существующей тестовой структуре, используемой для остальной части приложения.

(Кстати, я настоятельно рекомендую использовать QtCore даже для не-GUI частей приложений. Это гораздо приятнее работать.)

2

Если вы используете Qt, я бы рекомендовал использовать QtTest, потому что есть имеет средства для тестирования пользовательского интерфейса и прост в использовании.

Если вы используете QtCore, вы, вероятно, можете обойтись без STL. Я часто нахожу классы Qt более легкими в использовании, чем аналоги STL.

37

Вам не нужно создавать отдельные тестовые приложения. Просто используйте qExec в независимой основной функции() подобно этому:

int main(int argc, char *argv[]) 
{ 
    TestClass1 test1; 
    QTest::qExec(&test1, argc, argv); 

    TestClass2 test2; 
    QTest::qExec(&test2, argc, argv); 

    // ... 

    return 0; 
} 

Это будет выполнять все тестовые методы в каждом классе в одной партии.

ваших TestClass .h файлы будут выглядеть следующим образом:

class TestClass1 : public QObject 
{ 
Q_OBJECT 

private slots: 
    void testMethod1(); 
    // ... 
} 

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

+0

Мне нравится этот подход, но Im получает ошибку «Неизвестная тестовая функция:« test() ». Возможные совпадения: firstTest() ', любой отзыв? – Purefan

+2

qExec не следует вызывать более одного раза: поскольку он прерывает поддержку командной строки. Когда вы пишете тест с QtTest, вы можете перечислить все функции теста с опцией '-functions' и запустить отдельную функцию, передав ее в командной строке. Это прерывается, если qExec вызывается более одного раза, поскольку только первый вызов будет обрабатывать параметры командной строки. –

19

Чтобы добавить к ответу Джо.

Вот небольшой заголовок, который я использую (testrunner.h), содержащий класс утилиты, порождающий цикл событий (который, например, необходим для проверки подключений сигнальных слотов и баз данных в очереди) и «работающих» классов, совместимых с QTest :

#ifndef TESTRUNNER_H 
#define TESTRUNNER_H 

#include <QList> 
#include <QTimer> 
#include <QCoreApplication> 
#include <QtTest> 

class TestRunner: public QObject 
{ 
    Q_OBJECT 

public: 
    TestRunner() 
     : m_overallResult(0) 
    {} 

    void addTest(QObject * test) { 
     test->setParent(this); 
     m_tests.append(test); 
    } 

    bool runTests() { 
     int argc =0; 
     char * argv[] = {0}; 
     QCoreApplication app(argc, argv); 
     QTimer::singleShot(0, this, SLOT(run())); 
     app.exec(); 

     return m_overallResult == 0; 
    } 
private slots: 
    void run() { 
     doRunTests(); 
     QCoreApplication::instance()->quit(); 
    } 
private: 
    void doRunTests() { 
     foreach (QObject * test, m_tests) { 
      m_overallResult|= QTest::qExec(test); 
     } 
    } 

    QList<QObject *> m_tests; 
    int m_overallResult; 
}; 

#endif // TESTRUNNER_H 

Используйте это так:

#include "testrunner.h" 
#include "..." // header for your QTest compatible class here 

#include <QDebug> 

int main() { 
    TestRunner testRunner; 
    testRunner.addTest(new ...()); //your QTest compatible class here 

    qDebug() << "Overall result: " << (testRunner.runTests()?"PASS":"FAIL"); 

    return 0; 
} 
+1

хорошо работа! должен быть QCoreApplication снова в закрытом слоте ... – relascope

+0

Спасибо, исправлено! Еще не проверено, но скоро (как я использую вариант этого кода всякий раз, когда мне нужны тесты :)) – mlvljr

+0

Это действительно здорово! – Terrabits

18

Я начал с использованием QtTest для моего приложения и очень, очень быстро начал работать в ограничениях с ним. Две основные проблемы:

1) Мои тесты выполняются очень быстро - достаточно быстро, что накладные расходы на загрузку исполняемого файла, настройка приложения Q (Core) (при необходимости) и т. Д. Часто затмевают время работы самих тестов ! Связывание каждого исполняемого файла занимает много времени.

Накладные расходы продолжали расти по мере того, как все больше и больше классов были добавлены, и вскоре это стало проблемой - одна из целей модульных тестов - иметь защитную сетку, которая работает так быстро, что она не является бременем , и это быстро не соответствовало действительности. Решение состоит в том, чтобы объединить несколько наборов тестов в один исполняемый файл, а в то время как (как показано выше) это, в основном, возможно, это not supported и имеет важные ограничения.

2) Отсутствие поддержки крепежа - разблокировка сделки для меня.

Итак, через какое-то время я перешел на Google Test - это гораздо более функциональная и сложная модульная система тестирования (особенно при использовании с Google Mock) и решает 1) и 2), и, кроме того, вы все еще можете легко использовать удобные функции QTestLib, такие как QSignalSpy и симуляция графических интерфейсов и т. д. Было немного больно переключиться, но, к счастью, проект не продвинулся слишком далеко, и многие из этих изменений могут быть автоматизированы.

Лично я не буду использовать QtTest над Google Test для будущих проектов - если не предлагает никаких реальных преимуществ, которые я вижу, и имеет важные недостатки.

+0

Любые советы по объединению Qt и gtest? IE: у вас все еще есть QApplication или QMainWindow? Встраиваете ли вы свои тесты непосредственно в основную часть или в функцию-член некоторого потомка QObject? – KeyserSoze

+3

@KeyserSoze Все тесты конца и конца, которые требуют QWidgets, имеют QApplication; это QApplication создается (но не exec() 'd) в main(), прежде чем я буду RUN_ALL_TESTS(). QMainWindow можно использовать, но я в основном использую его только в своих сквозных тестах. Сами тесты соответствуют стандартной схеме тестирования Google, и у меня обычно есть, например,все модульные тесты для класса X в файле Xtests.cpp. Таким образом, это, по сути, стандартный проект gtest с несколькими уступками Qt (создание QApplication до запуска тестов обычным способом). –

+0

Я не знаю, было ли это недавно, но Qt Test позволяет добавить два закрытых слота [init] (http://doc.qt.io/qt-5/qtest-overview.html#creating-a-test) и [очистить ] (http://doc.qt.io/qt-5/qtest-overview.html#creating-a-test), которые вызываются до и после каждой тестовой функции соответственно. – markand

1

Я только что играл с этим. Главное преимущество использования Google Test для QtTest для нас в том, что мы делаем все наши разработки пользовательского интерфейса в Visual Studio. Если вы используете Visual Studio 2012 и установите Google Test Adapter, вы можете получить VS для распознавания тестов и включить их в свой тестовый проводник. Это отлично подходит разработчикам для использования при написании кода, а поскольку Google Test переносится, мы также можем добавить тесты в конец нашей сборки Linux.

Я надеюсь, что в будущем кто-то добавит поддержку C++ в один из параллельных инструментов тестирования, что C# есть, как NCrunch, Giles и ContinuousTests.

Конечно, вы можете найти, что кто-то пишет еще один адаптер для VS2012, который добавляет поддержку QtTest тестовому адаптеру, и в этом случае это преимущество уходит! Если кто-то заинтересован в этом, есть хорошая запись в блоге Authoring a new Visual studio unit test adapter.

3

Продлить mlvljr и решение Джо мы можем даже поддерживать полный набор опций QtTest за один тестовый класс и по-прежнему работать все в одном пакете плюс протоколирования:

usage: 
    help:          "TestSuite.exe -help" 
    run all test classes (with logging):   "TestSuite.exe" 
    print all test classes:      "TestSuite.exe -classes" 
    run one test class with QtTest parameters: "TestSuite.exe testClass [options] [testfunctions[:testdata]]... 

Заголовок

#ifndef TESTRUNNER_H 
#define TESTRUNNER_H 

#include <QList> 
#include <QTimer> 
#include <QCoreApplication> 
#include <QtTest> 
#include <QStringBuilder> 

/* 
Taken from https://stackoverflow.com/questions/1524390/what-unit-testing-framework-should-i-use-for-qt 
BEWARE: there are some concerns doing so, see https://bugreports.qt.io/browse/QTBUG-23067 
*/ 
class TestRunner : public QObject 
{ 
    Q_OBJECT 

public: 
    TestRunner() : m_overallResult(0) 
    { 
     QDir dir; 
     if (!dir.exists(mTestLogFolder)) 
     { 
     if (!dir.mkdir(mTestLogFolder)) 
      qFatal("Cannot create folder %s", mTestLogFolder); 
     } 
    } 

    void addTest(QObject * test) 
    { 
     test->setParent(this); 
     m_tests.append(test); 
    } 

    bool runTests(int argc, char * argv[]) 
    { 
     QCoreApplication app(argc, argv); 
     QTimer::singleShot(0, this, SLOT(run())); 
     app.exec(); 

     return m_overallResult == 0; 
    } 

    private slots: 
    void run() 
    { 
     doRunTests(); 
     QCoreApplication::instance()->quit(); 
    } 

private: 
    void doRunTests() 
    { 
     // BEWARE: we assume either no command line parameters or evaluate first parameter ourselves 
     // usage: 
     // help:          "TestSuite.exe -help" 
     // run all test classes (with logging):   "TestSuite.exe" 
     // print all test classes:      "TestSuite.exe -classes" 
     // run one test class with QtTest parameters: "TestSuite.exe testClass [options] [testfunctions[:testdata]]... 
     if (QCoreApplication::arguments().size() > 1 && QCoreApplication::arguments()[1] == "-help") 
     { 
     qDebug() << "Usage:"; 
     qDebug().noquote() << "run all test classes (with logging):\t\t" << qAppName(); 
     qDebug().noquote() << "print all test classes:\t\t\t\t" << qAppName() << "-classes"; 
     qDebug().noquote() << "run one test class with QtTest parameters:\t" << qAppName() << "testClass [options][testfunctions[:testdata]]..."; 
     qDebug().noquote() << "get more help for running one test class:\t" << qAppName() << "testClass -help"; 
     exit(0); 
     } 

     foreach(QObject * test, m_tests) 
     { 
     QStringList arguments; 
     QString testName = test->metaObject()->className(); 

     if (QCoreApplication::arguments().size() > 1) 
     { 
      if (QCoreApplication::arguments()[1] == "-classes") 
      { 
       // only print test classes 
       qDebug().noquote() << testName; 
       continue; 
      } 
      else 
       if (QCoreApplication::arguments()[1] != testName) 
       { 
        continue; 
       } 
       else 
       { 
        arguments = QCoreApplication::arguments(); 
        arguments.removeAt(1); 
       } 
     } 
     else 
     { 
      arguments.append(QCoreApplication::arguments()[0]); 
      // log to console 
      arguments.append("-o"); arguments.append("-,txt"); 
      // log to file as TXT 
      arguments.append("-o"); arguments.append(mTestLogFolder % "/" % testName % ".log,txt"); 
      // log to file as XML 
      arguments.append("-o"); arguments.append(mTestLogFolder % "/" % testName % ".xml,xunitxml"); 
     } 
     m_overallResult |= QTest::qExec(test, arguments); 
     } 
    } 

    QList<QObject *> m_tests; 
    int m_overallResult; 
    const QString mTestLogFolder = "testLogs"; 
}; 

#endif // TESTRUNNER_H 

собственный код

#include "testrunner.h" 
#include "test1" 
... 

#include <QDebug> 

int main(int argc, char * argv[]) 
{ 
    TestRunner testRunner; 

    //your QTest compatible class here 
    testRunner.addTest(new Test1); 
    testRunner.addTest(new Test2); 
    ... 

    bool pass = testRunner.runTests(argc, argv); 
    qDebug() << "Overall result: " << (pass ? "PASS" : "FAIL"); 

    return pass?0:1; 
} 
0

Для поддержки инструментального средства адаптера Visual Studio с использованием рамок QtTest это расширение Visual Studio: https://visualstudiogallery.msdn.microsoft.com/cc1fcd27-4e58-4663-951f-fb02d9ff3653

+0

Добро пожаловать в переполнение стека! Хотя эта ссылка может ответить на вопрос, лучше включить здесь основные части ответа и предоставить ссылку для справки. Ответные ссылки могут стать недействительными, если связанная страница изменится. См. [Как написать хороший ответ] (http://stackoverflow.com/help/how-to-answer). – ByteHamster

3

I блок тестировал наши библиотеки с использованием gtest и QSignalSpy. Используйте QSignalSpy для обнаружения сигналов. Вы можете вызывать слоты напрямую (например, обычные методы) для их проверки.

Смежные вопросы