web-dev-qa-db-fra.com

Comment le code de test unitaire C ++ doit-il être organisé pour une efficacité maximale du test unitaire?

  • Cette question est pas sur les cadres de tests unitaires.
  • Cette question est pas sur l'écriture des tests unitaires.
  • Cette question concerne pour mettre le code UT écrit et comment/quand/où le compiler et l'exécuter).

Dans Travailler efficacement avec Legacy Code , Michael Feathers affirme que

bons tests unitaires ... courir vite

et cela

Un test unitaire qui prend 1/10e de seconde pour s'exécuter est un test unitaire lent.

Je pense que ces définitions ont du sens. Je pense aussi qu'ils impliquent que vous devez garder un ensemble de Tests unitaires et un ensemble de Ces tests de code qui prennent plus de temps séparément, mais je suppose que c'est le prix que vous payez pour appeler un test unitaire uniquement s'il fonctionne (très) rapidement.

Évidemment, le problème en C++ est que pour "exécuter" votre test unitaire ( s ), vous devez:

  1. Modifier votre code (production ou test unitaire, selon le "cycle" dans lequel vous vous trouvez)
  2. Compiler
  3. Lien
  4. Lancer le test unitaire exécutable ( s )

Éditer (après un étrange vote serré): Avant d'entrer dans les détails, je vais essayer de résumer le point ici:

Comment organiser efficacement le code de test unitaire C++, afin qu'il soit à la fois efficace d'éditer le code (de test) et d'exécuter le code de test?


Le premier problème consiste alors à décider de mettre le code de test unitaire de sorte que:

  • il est "naturel" de le modifier et de le visualiser en combinaison avec le code de production associé.
  • il est facile/rapide de démarrer le cycle de compilation pour l'unité votre modification actuelle

Le deuxième problème lié est alors quoi à compiler pour que le feedback soit instantané.

Options extrêmes:

  • Chaque Unit-Test-Test-Unit vit dans un fichier cpp séparé et ce fichier cpp est compilé + lié séparément (avec le fichier unitaire de code source qu'il teste) à un seul exécutable qui exécute ensuite ce test unitaire.
    • (+) Cela minimise le temps de démarrage (compilation + liaison!) Pour l'unité de test unique.
    • (+) Le test s'exécute super rapidement, car il ne teste qu'une seule unité.
    • (-) L'exécution de l'ensemble de la suite devra démarrer un bazillion de processus. Peut être un problème à gérer.
    • (-) Les frais généraux du démarrage du processus deviendront visibles
  • L'autre côté serait d'avoir - toujours - un fichier cpp par test, mais tous les fichiers cpp de test (avec le code qu'ils testent!) Sont liés en un seul exécutable (par module/par projet/choisissez votre choix).
    • (+) Le temps de compilation serait toujours correct, car seul le code modifié sera compilé.
    • (+) L'exécution de l'ensemble de la suite est facile, car il n'y a qu'un seul exe à exécuter.
    • (-) La suite mettra du temps à se lier, car chaque recompilation de n'importe quel objet déclenchera une reconnexion.
    • (-) (?) La combinaison prendra plus de temps à courir, bien que si tous les tests unitaires sont rapides, le temps devrait être OK.

Alors, comment sont gérés les tests unitaires C++ du monde réel ? Si je ne lance que ce genre de choses tous les soirs/toutes les heures, la deuxième partie n'a pas vraiment d'importance, mais la première partie, à savoir comment "coupler" le code UT au code de production, de sorte qu'il soit "naturel" pour les développeurs de garder les deux au point est toujours important, je pense. (Et si les développeurs ont le code UT au point, ils voudront l'exécuter, ce qui nous ramène à la deuxième partie .)

Histoires et expériences du monde réel appréciées !

Remarques:

  • Cette question laisse intentionnellement une plate-forme et un système de fabrication/projet non spécifiés.
  • Questions marquées UT & C++ est un excellent point de départ, mais malheureusement trop de questions, et surtout de réponses, sont trop centrées sur les détails ou sur des cadres spécifiques.
  • Il y a quelque temps, j'ai répondu à une question similaire sur la structure des tests unitaires de boost. Je trouve que cette structure fait défaut pour les "vrais" tests unitaires rapides. Et je trouve l'autre question trop étroite, d'où cette nouvelle question.
48
Martin Ba

Nous avons tous les tests unitaires (pour un module) dans un seul exécutable. Les tests sont regroupés. Je peux exécuter un seul test (ou certains tests) ou un groupe de tests en spécifiant un nom (test/groupe) sur la ligne de commande du lanceur de test. Le système de build peut exécuter le groupe "Build", le département de test peut exécuter "All". Le développeur peut mettre des tests dans un groupe comme "BUG1234", 1234 étant le numéro de suivi des problèmes de l'affaire sur laquelle il travaille.

6
ur.

Tout d'abord, je ne suis pas d'accord avec "1) Modifier votre code (de production) et votre test unitaire". Vous ne devez modifier qu'un seul à la fois, sinon si le résultat change, vous ne saurez pas lequel l'a causé.

J'aime mettre des tests unitaires dans une arborescence de répertoires qui assombrit l'arborescence principale. Si j'ai /sources/componentA/alpha/foo.cc et /objects/componentA/beta/foo.o, alors je veux quelque chose comme /UTest_sources/componentA/alpha/test_foo.cc et /UTest_objects/componentA/beta/test_foo.o. J'utilise le même arbre d'ombre pour les objets stub/mock et toutes les autres sources dont les tests ont besoin. Il y aura des cas Edge, mais ce schéma simplifie beaucoup les choses. Une bonne macro d'édition peut tirer la source de test à côté de la source du sujet sans effort. Un bon système de construction (par exemple GNUMake) peut compiler les deux et exécuter le test avec une seule commande (par exemple make test_foo), et peut gérer un bazillion de tels processus - juste ceux dont les sources ont changé depuis leur dernier test - assez facilement (je n'ai jamais trouvé la surcharge de démarrage de ces processus être un problème, c'est O (N) ).

Dans le même cadre, vous pouvez avoir des tests à plus grande échelle (plus de tests unitaires) qui relient de nombreux objets entre eux et exécutent de nombreux tests. L'astuce consiste à trier ces tests en fonction du temps nécessaire à leur création/exécution et à les intégrer à votre programme quotidien en conséquence. Exécutez le test d'une seconde ou moins chaque fois que vous en avez envie; commencer le test de dix secondes et étirer; test de cinq minutes et faire une pause; tester une demi-heure et aller déjeuner; test de six heures et rentrez chez vous. Si vous trouvez que vous perdez beaucoup de temps, par exemple relier un énorme test après avoir changé un seul petit fichier, vous le faites mal - même si la liaison était instantanée, vous exécuteriez toujours un long test quand il n'était pas demandé.

6
Beta