Подтвердить что ты не робот

Qt: Как организовать Unit Test с несколькими классами?

У меня есть проект Qt Unit test (sub), который генерирует мне один класс (с основным сгенерированным QTEST_APPLESS_MAIN). Я могу запустить это из Qt Creator в качестве консольного приложения.

В: Как добавить дополнительные классы в качестве тестовых примеров для этого конкретного проекта.

  • Если в этих классах есть только "тестовые" слоты (private Q_SLOTS), методы не вызывают, а только те, что относятся к классу с QTEST_APPLESS_MAIN
  • Так как может быть только один main(..), я не могу использовать QTEST_APPLESS_MAIN с более чем одним классом в проекте (это правильно?)
  • Конечно, я могу вручную "проложить" слоты в (дополнительных) классах с одним классом, содержащим main, но это очень утомительно.

Итак, каков наилучший способ запустить Unit test по нескольким классам в проекте Unit test?

PS: В "Использование тестов QT Unit в проекте - конфликтующие основные функции (...)" a Блог упоминается, однако я не могу загрузить zip, описывающий решение.

Qt Unit Test subproject

4b9b3361

Ответ 1

В соответствии с решением, с которым вы связались, способ тестирования двух (или более) классов в рамках одного проекта Qt unit test заключается в обеспечении того, чтобы каждый тестируемый класс имел соответствующий тестовый класс и что вы создал пользовательский int main, который выполняет каждый тестовый класс.

Например:

class TestClassA : public QObject
{
   Q_OBJECT
public:
   TestClassA();

   ...

private Q_SLOTS:
   void testCase1();
   ...
};

class TestClassB : public QObject
{
   Q_OBJECT
public:
   TestClassB();

   ...

private Q_SLOTS:
   void testCase2();
   ...
};

void TestClassA::testCase1()
{
   // Define test here.
}

void TestClassB::testCase2()
{
   // Define test here.
}

// Additional tests defined here.

// Note: This is equivalent to QTEST_APPLESS_MAIN for multiple test classes.
int main(int argc, char** argv)
{
   int status = 0;
   {
      TestClassA tc;
      status |= QTest::qExec(&tc, argc, argv);
   }
   {
      TestClassB tc;
      status |= QTest::qExec(&tc, argc, argv);
   }
   return status;
}

Очевидно, что различные тестовые классы могут быть распределены по нескольким единицам перевода, а затем просто включены в блок перевода с вашим int main. Не забудьте включить соответствующие файлы .moc.

Ответ 2

Основываясь на принятом ответе, и если вы используете С++ 11, вас может заинтересовать решение с использованием лямбда-выражений. Это позволяет избежать написания одного и того же кода каждый раз. Хотя вы можете заменить лямбду функцией, я думаю, что лямбда чище.

#include <QtTest>

#include "test1.h"
#include "test2.h"


int main(int argc, char** argv)
{
   int status = 0;
   auto ASSERT_TEST = [&status, argc, argv](QObject* obj) {
     status |= QTest::qExec(obj, argc, argv);
     delete obj;
   };

   ASSERT_TEST(new Test1());
   ASSERT_TEST(new Test2());

   return status;
}

#ifndef TEST1_H
#define TEST1_H

Образец теста

#include <QtTest>

class Test1 : public QObject
{
    Q_OBJECT

  private Q_SLOTS:
    void testCase1();
};

Ответ 3

В поисках того же ответа я нашел очень хорошее решение из http://qtcreator.blogspot.de/2009/10/running-multiple-unit-tests.html. Он создает пространство имен с контейнером, в котором регистрируются все созданные тесты (с помощью макроса DECLARE_TEST), а затем использует его для запуска всех тестов в списке. Я переписал его, чтобы он соответствовал моему коду, и выкладываю свою версию здесь (версия My Qt Creator: 4.1.0):

/* BASED ON
 * http://qtcreator.blogspot.de/2009/10/running-multiple-unit-tests.html
 */    
#ifndef TESTCOLLECTOR_H
#define TESTCOLLECTOR_H

#include <QtTest>
#include <memory>
#include <map>
#include <string>

namespace TestCollector{
typedef std::map<std::string, std::shared_ptr<QObject> > TestList;
inline TestList& GetTestList()
{
   static TestList list;
   return list;
}

inline int RunAllTests(int argc, char **argv) {
    int result = 0;
    for (const auto&i:GetTestList()) {
        result += QTest::qExec(i.second.get(), argc, argv);
    }
    return result;
}

template <class T>
class UnitTestClass {
public:
    UnitTestClass(const std::string& pTestName) {
        auto& testList = TestCollector::GetTestList();
        if (0==testList.count(pTestName)) {
            testList.insert(std::make_pair(pTestName, std::make_shared<T>()));
        }
    }
};
}

#define ADD_TEST(className) static TestCollector::UnitTestClass<className> \
    test(#className);

#endif // TESTCOLLECTOR_H

Затем просто добавьте строку ADD_TEST (class) в заголовок теста следующим образом:

#ifndef TESTRANDOMENGINES_H
#define TESTRANDOMENGINES_H

#include <QtTest>
#include "TestCollector.h"

class TestRandomEngines : public QObject
{
    Q_OBJECT

private Q_SLOTS:
    void test1();
};

ADD_TEST(TestRandomEngines)

#endif // TESTRANDOMENGINES_H

И чтобы запустить все тесты, просто выполните:

#include "TestCollector.h"
#include <iostream>

int main(int argc, char *argv[]) {
    auto nFailedTests = TestCollector::RunAllTests(argc, argv);
    std::cout << "Total number of failed tests: "
              << nFailedTests << std::endl;
    return nFailedTests;
}

Ответ 4

Как я это делаю:

  • Создайте общий проект "subdirs".
  • Поместите тестируемый код в подпроект библиотеки С++.
  • Вместо использования проекта unit test я использую подпроект консольного приложения.
  • Свяжите библиотеку с этим консольным приложением, не забудьте обработать зависимости в файле .pro в верхней части иерархии.
  • В этом подпроекте консоли укажите столько классов тестов, сколько пожелаете, и запустите их в основном из этого же проекта.

Я в основном сделал небольшое изменение этот пост.

Ответ 5

Я использую следующий код для сбора всех результатов теста:

#include "testclassa.h"
#include "testclassb.h"
#include <QtTest>
#include <QDebug>

int main(int argc, char** argv){

    int failedTests = 0;
    TestClassA testClassA
    TestClassB testClassB
    failedTests += QTest::qExec(&testClassA, argc, argv);
    failedTests += QTest::qExec(&testClassB, argc, argv);

    if(failedTests > 0){
        qDebug() << "total number of failed tests: " << failedTests;
    }else{
        qDebug() << "all tests passed :)";
    }
    return failedTests;
}