Question

J'ai commencé à écrire des tests avec le système de test unitaire de Qt.

Comment organisez-vous habituellement les tests? Il est une classe d'essai par une classe de module, ou avez-vous tester le module entier avec une classe de test unique? docs Qt suggèrent de suivre l'ancienne stratégie.

Je veux écrire des tests pour un module. Le module ne fournit qu'une seule classe qui va être utilisé par l'utilisateur du module, mais il y a beaucoup de logique abstraite dans d'autres classes, que je voudrais aussi à tester, en plus de tester la classe publique.

Le problème est que la manière proposée par Qt pour exécuter des tests impliqués la macro QTEST_MAIN:

QTEST_MAIN(TestClass)
#include "test_class.moc"

et éventuellement un programme de test est capable de tester une seule classe de test. Et il suce un peu pour créer des projets de test pour chaque classe dans le module.

Bien sûr, on pourrait jeter un oeil à la macro QTEST_MAIN, réécrire et exécuter d'autres classes de test. Mais est-il quelque chose, qui fonctionne hors de la boîte?

Jusqu'à présent, je le fais à la main:

#include "one.h"
#include "two.h"

int main(int argc, char *argv[]) 
{ 
    QCoreApplication app(argc, argv); 
    TestOne one;
    QTest::qExec(&one, argc, argv);
    TestOne two;
    QTest::qExec(&two, argc, argv);
}
Était-ce utile?

La solution

Ouais, les forces QTest bit structure de test étrange et est généralement inférieur à Google Test / cadre de simulation. Pour un projet, je suis obligé d'utiliser QTest (exigence du client), et voici comment je l'utilise:

  1. Je compile tous les tests ensemble comme un modèle de projet subdir
  2. Pour créer de nouveaux tests plus facile, je partage beaucoup de la configuration du projet en utilisant le fichier common.pri j'inclus dans le dossier de chaque test
  3. Si possible, je partage les fichiers objet répertoire pour accélérer la compilation
  4. Je les Rassemblez tous en utilisant un lot + awk + script sed.

La création de ce quatre points est très facile et rend l'utilisation de QTest presque agréable. Avez-vous des problèmes avec l'exécution de plusieurs tests qui ne sont pas résolus par la configuration décrites ci-dessus?

PS:. L'exécution des tests comme vous le faites, à savoir appeler plusieurs QTest :: qExec provoque des problèmes avec commande -o commutateur de ligne - vous obtiendrez des résultats que pour la dernière classe testé

Autres conseils

associés à la réponse affichée par @cjhuitt

Ceci est un exemple qui élimine la nécessité d'appeler manuellement chaque objet de test

J'essaie d'éviter ce genre de choses:

MyTestClass1 t1;   t1.run();
MyTestClass2 t2;   t2.run();
//etc...

Ma solution est de laisser le test des objets hériter d'une classe de base qui s'ajoute à une liste statique Le programme principal exécute ensuite tous les objets de test dans cette liste. De cette façon, aucun des besoins de code-cadre de soutien à modifier. Les seules choses qui changent sont les classes de test eux-mêmes.

Voici comment je le fais:

qtestsuite.h - classe de base pour le test des objets

#ifndef QTESTSUITE_H
#define QTESTSUITE_H

#include <QObject>
#include <vector>

class QTestSuite : public QObject
{
    Q_OBJECT
public:
    static std::vector<QObject*> m_suites;

public:
    explicit QTestSuite();

};

#endif // QTESTSUITE_H

qtestsuite.cpp

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

std::vector<QObject*> QTestSuite::m_suites;

QTestSuite::QTestSuite() : QObject()
{
    m_suites.push_back(this);
}

testall.cpp - exécute les tests

#include "qtestsuite.h"

#include <QtTest/QtTest>
#include <iostream>

int main(int, char**)
{
    int failedSuitesCount = 0;
    std::vector<QObject*>::iterator iSuite;
    for (iSuite = QTestSuite::m_suites.begin(); iSuite != QTestSuite::m_suites.end(); iSuite++)
    {
        int result = QTest::qExec(*iSuite);
        if (result != 0)
        {
            failedSuitesCount++;
        }
    }
    return failedSuitesCount;
}

mytestsuite1.cpp - un objet de test par exemple, créer plus de ces

#include "qtestsuite.h"

#include <QtTest/QtTest>

class MyTestSuite1: public QTestSuite
{
     Q_OBJECT
private slots:
    void aTestFunction();
    void anotherTestFunction();
};

void MyTestSuite1::aTestFunction()
{
    QString str = "Hello";
    QVERIFY(str.toUpper() == "this will fail");
}

void MyTestSuite1::anotherTestFunction()
{
    QString str = "Goodbye";
    QVERIFY(str.toUpper() == "GOODBYE");
}

static MyTestSuite1 instance;  //This is where this particular test is instantiated, and thus added to the static list of test suites

#include "mytestsuite1.moc"

aussi, pour créer le fichier .pro

qmake -project "CONFIG += qtestlib"

Dans notre configuration avec QTest, nous avons fait quelques petites choses à faire mieux.

  • définir une sous-classe de QObject qui est utilisé comme une classe de base pour toute nouvelle classe de test d'unité.
  • Dans le constructeur pour cette classe, nous ajoutons l'instance de test à une liste statique de tests, et dans le destructor nous l'enlever.
  • Nous avons alors une fonction statique que les boucles à travers les tests et les pistes en utilisant QTest::qExec(). (Nous accumulons les valeurs retournées à chaque fois, et le retour que de notre fonction.)
  • main() appelle cette fonction, et renvoie le résultat que le succès / échec.
  • Enfin, dans l'unité de compilation du test spécifique lui-même, nous comprennent généralement une instance statique de cette classe.

Ce moyen de configuration que la classe sera instancié avant main() est exécuté, il sera ajouté à la liste des classes à tester lorsque des courses principales. Le cadre exige que vous avez juste besoin d'hériter votre classe correctement et instancier une instance statique si vous voulez toujours courir.

Nous créons aussi de temps en temps d'autres épreuves facultatives, qui sont ajoutés en fonction des commutateurs de ligne de commande.

En général, j'organise des tests avec un exécutable de test par classe en cours de test.

  

et éventuellement un programme de test est   capable de tester un seul test   classe.

Ceci est une bonne chose. Il isole vos tests les uns des autres, ce qui empêche les choses comme un accident lors d'un test de bloquer tous vos autres tests. Cet accident pourrait être causé par une composante commune dans plusieurs classes en cours de test. Le modèle des échecs alors vous sur la piste de l'origine sous-jacente du problème. En gros, vous avez de meilleures informations de diagnostic des pannes si vos tests sont indépendants les uns des autres.

Facilitez de configurer plusieurs exécutables et exécuter chaque test séparément. Utilisez un coureur de test pour tous les spawn de processus de test.

Mise à jour:

J'ai changé d'avis sur ce peu. Une fois que vous avez un grand programme avec beaucoup de tests, reliant des centaines de tests exécutables devient très lent. Ma nouvelle préférence est de mettre tous les tests pour une bibliothèque dans un fichier exécutable et de choisir les tests à l'aide d'arguments invoquer la ligne de commande transmis à l'exécutable de test.

Cela permet de réduire le nombre des executables de quelques centaines à des dizaines, mais conserve les avantages de l'exécution des tests séparément.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top