Jakiego frameworka do testowania jednostek powinienem użyć dla Qt? [zamknięte]

Właśnie rozpoczynam nowy projekt, który wymaga wieloplatformowego GUI, a jako Framework wybraliśmy Qt.

Potrzebujemy też frameworka do testowania jednostek. Jeszcze około rok temu korzystaliśmy z opracowanego przez siebie frameworka do testowania jednostek dla projektów C++, ale obecnie przechodzimy do korzystania z Google Test dla nowych projektów.

Czy ktoś ma jakieś doświadczenie z używaniem Google Test dla aplikacji Qt? Czy QtTest/QTestLib to lepsza alternatywa?

I am still not sure jak bardzo chcemy używać Qt w nie-GUI części projektu-prawdopodobnie wolelibyśmy po prostu użyć STL / Boost w kodzie core z małym interfejsem do GUI opartego na Qt.

EDIT: wygląda na to, że wielu skłania się ku QtTest. Czy jest ktoś, kto ma jakieś doświadczenie z integracją tego z serwerem continous integration? Wydaje mi się również, że konieczność obsługi osobnej aplikacji dla każdego nowego przypadku testowego spowodowałaby wiele tarć. Czy jest jakiś dobry sposób na rozwiązać? Czy Qt Creator ma dobry sposób radzenia sobie z takimi przypadkami testowymi, czy też potrzebujesz projektu na jeden przypadek testowy?

Author: kiamlaluno, 2009-10-06

11 answers

Nie wiem czy QTestLib jest "lepszy"niż jeden framework dla drugiego w tak ogólnych kategoriach. Jest jedna rzecz, która robi to dobrze, a to zapewnia dobry sposób na testowanie aplikacji opartych na Qt.

Możesz zintegrować QTest z nową konfiguracją opartą na Google Test. Nie próbowałem tego, ale w oparciu o architekturę QTestLib wydaje się, że nie byłoby to zbyt skomplikowane.

Testy pisane czystym QTestLib mają opcję-xml, której możesz użyć, wraz z niektórymi XSLT transformacje w celu konwersji do formatu wymaganego dla serwera integracji ciągłej. Jednak wiele z tego zależy od tego, z którym serwerem CI pójdziesz. Wyobrażam sobie, że to samo dotyczy GTest.

Pojedyncza aplikacja testowa na przypadek testowy nigdy nie powodowała dla mnie dużych tarć, ale zależy to od posiadania systemu budowania, który wykonałby przyzwoitą pracę zarządzania budynkiem i realizacją przypadków testowych.

Nie znam nic w Qt Creator, co wymagałoby oddzielnego projektu na test przypadek, ale mogło się to zmienić od czasu ostatniego spojrzenia na Qt Creator.

Sugerowałbym również trzymanie się QtCore i trzymanie się z dala od STL. Korzystanie z QtCore ułatwi radzenie sobie z bitami GUI, które wymagają typów danych Qt. W takim przypadku nie musisz się martwić o konwersję z jednego typu danych na inny.

 17
Author: Matt Rogers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-02 19:45:08

Nie musisz tworzyć oddzielnych aplikacji testowych. Wystarczy użyć qExec w niezależnej funkcji main () podobnej do tej:

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

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

    // ...

    return 0;
}

Spowoduje to wykonanie wszystkich metod testowych w każdej klasie w jednej partii.

Twoja klasa testowa .pliki h wyglądałyby następująco:
class TestClass1 : public QObject
{
Q_OBJECT

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

Niestety ta konfiguracja nie jest dobrze opisana w dokumentacji Qt, mimo że wydaje się być bardzo przydatna dla wielu ludzi.

 37
Author: Joe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-27 14:51:43

Aby dołączyć do odpowiedzi Joego.

Oto mały nagłówek, którego używam (testrunner.h), zawierający klasę użytkową wywołującą pętlę zdarzeń (która jest na przykład potrzebna do testowania połączeń z gniazdami sygnału i baz danych w kolejce) oraz" uruchamiające " klasy kompatybilne z 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

Użyj go tak:

#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;
}
 19
Author: mlvljr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-26 20:23:26

Zacząłem używać QtTest dla mojej aplikacji i bardzo, bardzo szybko zacząłem z nią korzystać. Dwa główne problemy to:

1) moje testy działają bardzo szybko - na tyle szybko, że obciążenie programu wykonywalnego, konfiguracja aplikacji Q (Core) (w razie potrzeby) itp. często zmniejsza czas wykonania samych testów! Łączenie KAŻDEGO pliku wykonywalnego zajmuje dużo czasu.

Koszty po prostu rosły wraz z dodawaniem kolejnych klas i wkrótce stało się problemem - jednym z celów testów jednostkowych jest posiadanie siatki bezpieczeństwa, która działa tak szybko, że nie jest wcale obciążeniem, a to szybko się nie stało. Rozwiązaniem jest scalanie wielu pakietów testowych w jeden plik wykonywalny, i chociaż (jak pokazano powyżej) jest to w większości możliwe do wykonania, to jest nie wspierany i ma ważne ograniczenia.

2) Brak oprawy-dla mnie przeróbka.

Więc po jakimś czasie przełączyłem się na Google Test - jest to o wiele bardziej funkcjonalne i zaawansowany framework do testowania jednostek (szczególnie w przypadku użycia z Google Mock) i rozwiązuje 1) i 2), a ponadto nadal można łatwo korzystać z przydatnych funkcji QTestLib, takich jak QSignalSpy i symulacja zdarzeń GUI itp. Zmiana była trochę uciążliwa, ale na szczęście projekt nie posunął się zbyt daleko i wiele zmian można było zautomatyzować.

Osobiście Nie będę używać QtTest nad Google Test dla przyszłych projektów-jeśli nie oferuje żadnych realnych korzyści, które widzę, i ma ważne wady.

 18
Author: SSJ_GZ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-29 12:08:48

Dlaczego nie używać frameworka unit-testing zawartego w Qt? Przykład: Qttestlib Tutorial .

 7
Author: Patrice Bernassola,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-25 13:39:54

QtTest jest głównie przydatny do testowania części, które wymagają QT event loop/signal dispatching. Jest zaprojektowany w taki sposób, że każdy przypadek testowy wymaga osobnego pliku wykonywalnego, więc nie powinien być w konflikcie z żadnymi istniejącymi frameworkami testowymi używanymi w pozostałej części aplikacji.

(Btw, Gorąco polecam korzystanie z QtCore nawet dla nie-GUI części aplikacji. Jest o wiele przyjemniej pracować.)

 3
Author: Lukáš Lalinský,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-06 08:59:48

Aby rozszerzyć rozwiązanie mlvljr i Joe, możemy nawet obsługiwać pełne opcje QtTest na jedną klasę testową i nadal uruchamiać wszystkie w partii plus logowanie:

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]]...

Header

#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

Własny kod

#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;
}
 3
Author: j.holetzeck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-03 11:22:01

Przetestowałem nasze biblioteki używając gtest i QSignalSpy . Użyj QSignalSpy do przechwytywania sygnałów. Możesz wywołać sloty bezpośrednio (jak normalne metody), aby je przetestować.

 3
Author: BЈовић,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-16 09:45:11

Jeśli używasz Qttest, polecam użycie QtTest, ponieważ ma możliwości testowania interfejsu użytkownika i jest prosty w użyciu.

Jeśli używasz QtCore, prawdopodobnie możesz obejść się bez STL. Często uważam, że Klasy Qt są łatwiejsze w użyciu niż ich odpowiedniki STL.

 2
Author: Gunther Piez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-06 09:51:30

Tylko się tym bawiłem. Główną zaletą korzystania z Google Test nad QtTest jest to, że cały nasz interfejs użytkownika tworzymy w Visual Studio. Jeśli korzystasz z Visual Studio 2012 i zainstalujesz Google Test Adapter , możesz uzyskać VS, aby rozpoznać testy i włączyć je do Eksploratora testów. Jest to świetne rozwiązanie dla programistów, którzy mogą używać go podczas pisania kodu, a ponieważ Google Test jest przenośny, możemy również dodać testy do końca naszej kompilacji Linuksa.

Mam nadzieję w przyszłości ktoś doda wsparcie dla C++ do jednego z równoległych narzędzi testowych, które posiada C#, np. NCrunch, Giles i ContinuousTests .

Oczywiście może ktoś napisać inny adapter do VS2012, który dodaje obsługę QtTest do testowania adaptera, w którym to przypadku ta zaleta przepada! Jeśli ktoś jest tym zainteresowany, to jest dobry wpis na blogu , w którym znajduje się nowy adapter testowy Visual studio .

 1
Author: parsley72,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-19 19:39:32

Do obsługi narzędzia Visual Studio test adapter z frameworkiem QtTest użyj tego rozszerzenia Visual Studio: https://visualstudiogallery.msdn.microsoft.com/cc1fcd27-4e58-4663-951f-fb02d9ff3653

 0
Author: user3395798,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-06 19:44:09