ما هي إطار اختبار الوحدات التي يجب أن أستخدمها ل QT؟ [مغلق

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

سؤال

أنا فقط بدأت مشروعا جديدا يحتاج إلى بعض واجهة المستخدم الرسومية عبر المنصة، وقد اختارنا كيو تي كإطار واجهة المستخدم الرسومية.

نحن بحاجة إلى إطار اختبار الوحدة أيضا. حتى قبل حوالي عام، استخدمنا إطارا لاختبار وحدة متطورة في المنزل لمشاريع C ++، لكننا نتحرك الآن لاستخدام اختبار Google للمشاريع الجديدة.

هل لدى أي شخص أي خبرة في استخدام اختبار Google للتطبيقات QT؟ هل Qttest / Qtestlib بديل أفضل؟

ما زلت غير متأكد من أننا نريد استخدام QT في الأجزاء غير واجهة المستخدم الرسومية للمشروع - ربما نفضل فقط استخدام STL / BOOST في التعليمات البرمجية الأساسية مع واجهة صغيرة إلى واجهة المستخدم الرسومية القائم على QT.

تعديل: يبدو أن الكثيرين يميلون نحو Qttest. هل هناك أي شخص لديه أي تجربة مع دمج هذا مع خادم التكامل المستمر؟ أيضا، يبدو لي أن الاضطرار إلى التعامل مع تطبيق منفصل لكل حالة اختبار جديدة سيؤدي إلى احتكاك الكثير من الاحتكاك. هل هناك أي طريقة جيدة لحل ذلك؟ هل لدى كيو تي كيتور طريقة جيدة لمعالجة حالات الاختبار هذه أو هل تحتاج إلى الحصول على مشروع لكل حالة اختبار؟

هل كانت مفيدة؟

المحلول

لا أعرف أن Qtestlib هو "أفضل" من إطار لآخر في هذه المصطلحات العامة. هناك شيء واحد أنه جيد، وهذا يوفر طريقة جيدة لاختبار التطبيقات القائمة على QT.

يمكنك دمج QTEST في الإعداد الجديد المستند إلى Google Test الخاص بك. لم أحاول ذلك، ولكن بناء على كيفية مشاركة Qtestlib، يبدو أنها لن تكون معقدة للغاية.

تحتوي الاختبارات المكتوبة ب Qtestlib النقي لديك خيار -xml يمكنك استخدامه، إلى جانب تحويل بعض التحولات XSLT إلى التنسيق المطلوب لخادم التكامل المستمر. ومع ذلك، فإن الكثير من ذلك يعتمد على خادم CI الذي تذهب إليه. وأود أن أتصور نفس الشيء ينطبق على GTEST.

لم تسبب تطبيق اختبار واحد لكل حالة اختبار أبدا في الكثير من الاحتكاك بالنسبة لي، لكن هذا يعتمد على وجود نظام بناء من شأنه أن يقوم بعمل مناسب لإدارة بناء وتنفيذ حالات الاختبار.

لا أعرف أي شيء في خالق QT الذي يتطلب مشروعا منفصلا لكل حالة اختبار، لكن كان من الممكن تغييره منذ آخر مرة نظرت إليها في كيو تي.

أود أن أقترح أيضا الشائكة مع Qtcore والبقاء بعيدا عن STL. باستخدام Qtcore طوال الوقت سيجعل التعامل مع BITs GUI التي تتطلب أنواع بيانات QT أسهل. لن تقلق بشأن التحويل من نوع بيانات إلى آخر في هذه الحالة.

نصائح أخرى

ليس عليك إنشاء تطبيقات اختبارات منفصلة. ما عليك سوى استخدام QEXEC في وظيفة رئيسية مستقلة () مماثلة لهذا واحد:

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

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

    // ...

    return 0;
}

سيقوم ذلك بتنفيذ جميع طرق الاختبار في كل فئة في دفعة واحدة.

ستبدو ملفات TestClass الخاصة بك كما يلي:

class TestClass1 : public QObject
{
Q_OBJECT

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

لسوء الحظ، لا يتم وصف هذا الإعداد جيدا حقا في وثائق كيو تي على الرغم من أنه يبدو أنه مفيد للغاية بالنسبة للكثير من الناس.

لإلحاق إجابة جو.

إليك رأس صغير أستخدمه (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 (أساسي) وما في كثير من الأحيان الأقزام في كثير من الأحيان وقت تشغيل الاختبارات أنفسهم! ربط كل واحد بالتنفيذ يشغل الكثير من الوقت أيضا.

تم الاحتفاظ بهذا النفقات العامة على زيادة أكبر قدر ممكن من الفصول الدراسية، وسرعان ما أصبحت مشكلة - إحدى أهداف اختبارات الوحدات هي أن يكون لديك شبكة أمان تعمل بسرعة كبيرة بحيث لا تكون عبئا على الإطلاق، وكان هذا أصبحت بسرعة ليست هي الحال. الحل هو الأجنحة اختبار متعددة في الأرض في واحد قابل للتنفيذ، وبينما (كما هو موضح أعلاه) هذا هو في الغالب قادرة، هو غير مدعوم ولديه قيود مهمة.

2) لا دعم لاعبا اساسيا - وكسر الصفقة بالنسبة لي.

بعد فترة من الوقت، لقد تحولت إلى اختبار Google - إنه إطار اختبار وحدة محمول ومتطور (خاصة عند استخدامه باستخدام Google Mock) وحل 1) و 2)، وعلاوة على ذلك، لا يزال بإمكانك استخدام ميزات Qtestlib مفيدة بسهولة مثل QSignalspy ومحاكاة أحداث واجهة المستخدم الرسومية، إلخ. كان من الألم قليلا من الألم، لكن لحسن الحظ لم تقدم المشروع بعيدا جدا وعلى الرغم من أن العديد من التغييرات يمكن أن تكون الآلي.

شخصيا، لن أستخدم Qttest على اختبار Google للمشاريع المستقبلية - إذا لم يقدم أي مزايا حقيقية أستطيع أن أرى، ولديها عيوب مهمة.

لماذا لا تستخدم إطار اختبار الوحدة المدرجة في كيو تي؟ مثال : 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;
}

إذا كنت تستخدم كيو تي، فسوف أوصي باستخدام Qttest، لأنه يحتوي على مرافق لاختبار UI وهو سهل الاستخدام.

إذا كنت تستخدم QTCore، فمن المحتمل أن تفعل ذلك بدون STL. كثيرا ما أجد فئات QT أسهل من نظيراتها STL.

لقد تلعب فقط مع هذا. الميزة الرئيسية لاستخدام اختبار Google عبر QTTest بالنسبة لنا هو أننا نفعل كل تطوير واجهة المستخدم في Visual Studio. إذا كنت تستخدم Visual Studio 2012 وتثبيت محول اختبار Google. يمكنك الحصول على مقابل التعرف على الاختبارات وتضمينها في مستكشف الاختبار. هذا رائع بالنسبة للمطورين لتتمكن من استخدام التعليمات البرمجية، ولأن اختبار Google محمول يمكننا أيضا إضافة الاختبارات إلى نهاية بناء Linux الخاص بنا.

آمل في المستقبل أن يضيف شخص ما دعما ل C ++ إلى أحد أدوات الاختبار المتزامنة التي تحتوي على C #، مثل ncrunch., جايلز و مستمرات.

بالطبع، قد تجد شخصا ما يكتب محول آخر ل VS2012 يضيف دعم Qttest إلى اختبار محول في هذه الحالة يذهب هذه الميزة بعيدا! إذا كان أي شخص مهتما بهذا هناك وظيفة مدونة جيدة تأليف محول اختبار وحدة Visual Studio الجديدة.

للحصول على دعم أداة اختبار محول Visual Studio مع إطار Qttest، استخدم ملحق Visual Studio: https://visualstudiogallery.msdn.microsoft.com/cc1fcd27-4e58-4663-951F-FB02D9FF3653.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top