Какой фреймворк модульного тестирования я должен использовать для Qt?[закрыто]

StackOverflow https://stackoverflow.com/questions/1524390

Вопрос

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

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

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

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

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

Это было полезно?

Решение

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

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

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

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

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

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

Другие советы

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

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, хотя многим она может показаться весьма полезной.

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

Вот небольшой заголовок, который я использую (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;
}

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

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

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

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

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

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

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

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

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

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

Чтобы расширить решение mlvljr и Joe, мы можем даже поддерживать полные параметры 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;
}

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

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

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

Я надеюсь, что в будущем кто-нибудь добавит поддержку C ++ в один из инструментов параллельного тестирования, которые есть в C #, например NCrunch ( Хруст ), Джайлс и Непрерывные тесты.

Конечно, вы можете обнаружить, что кто-то пишет другой адаптер для VS2012, который добавляет поддержку QtTest для Test Adapter, и в этом случае это преимущество исчезает!Если кому-то это интересно, есть хороший пост в блоге Создание нового адаптера модульного тестирования Visual Studio.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top