51

クロスプラットフォームのGUIを必要とする新しいプロジェクトを立ち上げたばかりで、GUIフレームワークとしてQtを選択しました。

ユニットテストのフレームワークも必要です。約1年前まで、C ++プロジェクトには社内で開発された単体テストフレームワークを使用していましたが、現在は新しいプロジェクトにGoogleTestを使用するように移行しています。

QtアプリケーションにGoogleTestを使用した経験のある人はいますか?QtTest / QTestLibはより良い代替手段ですか?

プロジェクトのGUI以外の部分でQtをどれだけ使用したいかはまだわかりません。おそらく、QtベースのGUIへの小さなインターフェイスを備えたコアコードでSTL/Boostを使用することをお勧めします。

編集:多くの人がQtTestに傾いているようです。これを継続的インテグレーションサーバーと統合した経験のある人はいますか?また、新しいテストケースごとに個別のアプリケーションを処理する必要があると、多くの摩擦が発生するように思われます。それを解決する良い方法はありますか?Qt Creatorにはそのようなテストケースを処理する良い方法がありますか、それともテストケースごとにプロジェクトを用意する必要がありますか?

4

11 に答える 11

38

個別のテストアプリケーションを作成する必要はありません。次のような独立したmain()関数でqExecを使用するだけです。

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

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

    // ...

    return 0;
}

これにより、各クラスのすべてのテストメソッドが1つのバッチで実行されます。

testclass.hファイルは次のようになります。

class TestClass1 : public QObject
{
Q_OBJECT

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

残念ながら、この設定は、多くの人にとって非常に役立つように見えますが、Qtのドキュメントでは実際にはうまく説明されていません。

于 2010-09-27T14:51:43.340 に答える
21

私は自分のアプリにQtTestを使い始めましたが、すぐに制限にぶつかり始めました。2つの主な問題は次のとおりです。

1)私のテストは非常に高速に実行されます-実行可能ファイルのロード、Q(コア)アプリケーションのセットアップ(必要な場合)などのオーバーヘッドがテスト自体の実行時間を短縮するほど十分に高速です!各実行可能ファイルのリンクにも多くの時間がかかります。

クラスが追加されるにつれてオーバーヘッドが増え続け、すぐに問題になりました。単体テストの目標の1つは、まったく負担にならないほど高速に動作するセーフティネットを用意することです。急速にそうではなくなりました。解決策は、複数のテストスイートを1つの実行可能ファイルにグロブすることです。これは(上記のように)ほとんど実行可能ですが、サポートされておらず、重要な制限があります。

2)フィクスチャのサポートはありません-私にとっては大きな問題です。

そこでしばらくして、Google Testに切り替えました。これははるかに機能的で洗練された単体テストフレームワークであり(特にGoogle Mockで使用する場合)、1)と2)を解決します。さらに、便利なQTestLib機能を簡単に使用できます。 QSignalSpyやGUIイベントのシミュレーションなど。切り替えるのは少し面倒でしたが、ありがたいことに、プロジェクトはそれほど進んでおらず、変更の多くは自動化できました。

個人的には、将来のプロジェクトでGoogle TestよりもQtTestを使用することはありません。私が見ることができる本当の利点がなく、重要な欠点がある場合。

于 2012-10-02T08:54:49.603 に答える
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;
}
于 2012-10-02T00:44:48.230 に答える
18

このような一般的な用語で、QTestLibが別のフレームワークよりも「優れている」ことを私は知りません。それがうまくいくことが1つあり、それはQtベースのアプリケーションをテストするための良い方法を提供します。

QTestを新しいGoogleTestベースのセットアップに統合できます。試したことはありませんが、QTestLibの設計方法からすると、それほど複雑ではないようです。

純粋なQTestLibで記述されたテストには、継続的インテグレーションサーバーに必要な形式に変換するためのいくつかのXSLT変換とともに、使用できる-xmlオプションがあります。ただし、その多くは、使用するCIサーバーによって異なります。同じことがGTestにも当てはまると思います。

テストケースごとに1つのテストアプリが私に大きな摩擦を引き起こすことはありませんでしたが、それはテストケースの構築と実行を管理する適切な仕事をするビルドシステムを持っているかどうかに依存します。

Qt Creatorには、テストケースごとに個別のプロジェクトが必要になるものは何もありませんが、前回QtCreatorを見たときから変更されている可能性があります。

また、QtCoreを使い続け、STLから離れることをお勧めします。全体でQtCoreを使用すると、Qtデータ型を必要とするGUIビットの処理が容易になります。その場合、あるデータ型から別のデータ型への変換について心配する必要はありません。

于 2009-10-06T17:42:21.493 に答える
6

Qtに含まれているユニットテストフレームワークを使用してみませんか?例:QtTestLibチュートリアル

于 2009-10-06T08:57:10.003 に答える
4

gtestとQSignalSpyを使用してライブラリを単体テストしました。QSignalSpyを使用して信号をキャッチします。(通常の方法のように)スロットを直接呼び出してテストすることができます。

于 2017-03-16T09:45:11.283 に答える
3

QtTestは、Qtイベントループ/シグナルディスパッチを必要とするパーツのテストに主に役立ちます。各テストケースに個別の実行可能ファイルが必要になるように設計されているため、アプリケーションの残りの部分で使用されている既存のテストフレームワークと競合しないようにする必要があります。

(ところで、アプリケーションのGUI以外の部分にもQtCoreを使用することを強くお勧めします。操作する方がはるかに優れています。)

于 2009-10-06T08:59:48.550 に答える
3

mlvljrとJoeのソリューションを拡張するために、1つのテストクラスごとに完全な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;
}
于 2015-07-03T11:22:01.680 に答える
2

Qtを使用している場合は、QtTestを使用することをお勧めします。これは、UIをテストする機能があり、使いやすいためです。

QtCoreを使用する場合は、おそらくSTLなしで実行できます。QtクラスはSTLクラスよりも使いやすいと思うことがよくあります。

于 2009-10-06T09:51:30.977 に答える
1

私はこれで遊んでいます。QtTestよりもGoogleTestを使用する主な利点は、すべてのUI開発をVisualStudioで行うことです。Visual Studio 2012を使用してGoogleテストアダプターをインストールすると、VSにテストを認識させ、テストエクスプローラーに含めることができます。これは、開発者がコードを作成するときに使用できる優れた機能です。GoogleTestは移植性があるため、Linuxビルドの最後にテストを追加することもできます。

将来的には、 NCrunchGilesContinuousTestsなどのC#が持つ同時テストツールの1つにC++のサポートが追加されることを期待しています。

もちろん、誰かがVS2012用に別のアダプターを作成してQtTestサポートをテストアダプターに追加していることに気付くかもしれません。その場合、この利点はなくなります。誰かがこれに興味があるなら、新しいVisualStudioユニットテストアダプターを作成する良いブログ投稿があります。

于 2013-02-13T02:44:10.447 に答える
0

QtTestフレームワークでのVisualStudioテストアダプターツールのサポートについては、次のVisual Studio拡張機能を使用してください:https ://visualstudiogallery.msdn.microsoft.com/cc1fcd27-4e58-4663-951f-fb02d9ff3653

于 2015-11-06T19:44:09.200 に答える