web-dev-qa-db-fra.com

Y a-t-il une raison pour laquelle les tests ne sont pas écrits en ligne avec le code qu'ils testent?

J'ai lu un peu sur Literate Programming récemment, et cela m'a fait réfléchir ... Des tests bien écrits, en particulier les spécifications de style BDD peuvent mieux expliquer ce que fait le code que la prose. , et ont le gros avantage de vérifier leur propre précision.

Je n'ai jamais vu de tests écrits en ligne avec le code qu'ils testent. Est-ce simplement parce que les langues n'ont pas tendance à simplifier la séparation du code de l'application et du test lorsqu'ils sont écrits dans le même fichier source (et personne ne facilite les choses), ou y a-t-il une raison plus logique que les gens séparent le code de test du code d'application?

92
Chris Devereux

Le seul avantage auquel je peux penser pour les tests en ligne serait de réduire le nombre de fichiers à écrire. Avec les IDE modernes, ce n'est vraiment pas si grave.

Il y a cependant un certain nombre d'inconvénients évidents aux tests en ligne:

  • Il viole séparation des préoccupations . Cela peut être discutable, mais pour moi, tester la fonctionnalité est une responsabilité différente de la mettre en œuvre.
  • Vous devrez soit introduire de nouvelles fonctionnalités de langage pour distinguer les tests/implémentations, soit risquer de brouiller la frontière entre les deux.
  • Les fichiers source plus volumineux sont plus difficiles à utiliser: plus difficiles à lire, plus difficiles à comprendre, vous êtes plus susceptible de devoir gérer des conflits de contrôle de source.
  • Je pense qu'il serait plus difficile de mettre votre chapeau de "testeur", pour ainsi dire. Si vous regardez les détails de l'implémentation, vous serez plus tenté de ne pas implémenter certains tests.
88
vaughandroid

Je peux penser à certains:

  • Lisibilité. L'entrecroisement de "vrai" code et de tests rendra la lecture du code plus difficile.

  • Code ballonnement. Mélanger le "vrai" code et le code de test dans les mêmes fichiers/classes/tout ce qui est susceptible d'entraîner des fichiers compilés plus volumineux, etc. Ceci est particulièrement important pour les langues avec liaison tardive.

  • Vous ne voudrez peut-être pas que vos clients/clients voient votre code de test. (Je n'ai pas comme cette raison ... mais si vous travaillez sur un projet de source fermée, le code de test est peu susceptible d'aider le client de toute façon.)

Il existe maintenant des solutions de contournement possibles pour chacun de ces problèmes. Mais à mon avis, il est plus simple de ne pas y aller en premier lieu.


Il vaut la peine d’observer qu’au début, Java faisaient ce genre de choses, par exemple en incluant une méthode main(...) dans une classe pour faciliter les tests. Cette idée a Il est d'usage dans l'industrie d'implémenter les tests séparément en utilisant un cadre de test quelconque.

Il convient également de noter que la programmation littéraire (telle que conçue par Knuth) n'a jamais fait son chemin dans l'industrie du génie logiciel.

36
Stephen C

En fait, vous pouvez penser à Design By Contract comme cela. Le problème est que la plupart des langages de programmation ne vous permettent pas d'écrire du code comme ceci :( Il est très facile de tester les conditions préalables à la main, mais les conditions de publication sont un vrai défi sans changer la façon dont vous écrivez le code (un énorme IMO négatif).

Michael Feathers a un présentation à ce sujet et c'est l'une des nombreuses façons dont il mentionne que vous pouvez améliorer la qualité du code.

14
Daniel Kaplan

Pour plusieurs des mêmes raisons que vous essayez d'éviter le couplage étroit entre les classes dans votre code, c'est également une bonne idée d'éviter le couplage inutile entre les tests et le code.

Création: Les tests et le code peuvent être écrits à différents moments, par différentes personnes.

Control: Si des tests sont utilisés pour spécifier des exigences, vous voudrez certainement qu'ils soient soumis à des règles différentes sur qui peut les changer et quand que le code réel est.

Réutilisation: Si vous mettez les tests en ligne, vous ne pouvez pas les utiliser avec un autre morceau de code.

Imaginez que vous avez un morceau de code qui fait le travail correctement, mais laisse beaucoup à désirer en termes de performances, de maintenabilité, peu importe. Vous décidez de remplacer ce code par du code nouveau et amélioré. L'utilisation du même ensemble de tests peut vous aider à vérifier que le nouveau code produit les mêmes résultats que l'ancien code.

Sélectionnabilité: Garder les tests séparés du code facilite le choix des tests que vous souhaitez exécuter.

Par exemple, vous pourriez avoir une petite suite de tests qui se rapportent uniquement au code sur lequel vous travaillez actuellement, et une plus grande suite qui teste l'ensemble du projet.

13
Caleb

Voici quelques raisons supplémentaires auxquelles je peux penser:

  • avoir des tests dans une bibliothèque séparée permet de lier uniquement cette bibliothèque à votre framework de test, et non à votre code de production (cela pourrait être évité par un préprocesseur, mais pourquoi construire une telle chose quand la solution la plus simple est d'écrire les tests dans un endroit séparé)

  • les tests d'une fonction, d'une classe, d'une bibliothèque sont généralement écrits du point de vue des "utilisateurs" (un utilisateur de cette fonction/classe/bibliothèque). Une telle "utilisation de code" est généralement écrite dans un fichier ou une bibliothèque séparée, et un test peut être plus clair ou "plus réaliste" s'il imite cette situation.

10
Doc Brown

Si les tests étaient en ligne, il serait nécessaire de supprimer le code dont vous avez besoin pour les tests lorsque vous expédiez le produit à votre client. Donc, un endroit supplémentaire où vous stockez vos tests sépare simplement le code vous besoin et le code dont ( votre client a besoin.

5
mhr

Cette idée revient simplement à une méthode "Self_Test" dans le contexte d'une conception basée sur un objet ou orientée objet. Si vous utilisez un langage basé sur les objets compilé comme Ada, tout le code d'auto-test sera marqué par le compilateur comme inutilisé (jamais invoqué) pendant la compilation de production, et par conséquent, tout sera optimisé - rien ne s'affichera dans le exécutable résultant.

L'utilisation d'une méthode "Self_Test" est une très bonne idée, et si les programmeurs étaient vraiment soucieux de la qualité, ils le feraient tous. Un problème important, cependant, est que la méthode "Self_Test" doit avoir une discipline intense, en ce sens qu'elle ne peut accéder à aucun des détails d'implémentation et doit à la place s'appuyer uniquement sur toutes les autres méthodes publiées dans la spécification de l'objet. De toute évidence, si l'autotest échoue, la mise en œuvre devra changer. L'autotest doit tester rigoureusement toutes les propriétés publiées des méthodes de l'objet, mais ne jamais s'appuyer en aucune façon sur les détails d'une implémentation spécifique.

Les langages orientés objet et orienté objet fournissent fréquemment exactement ce type de discipline en ce qui concerne les méthodes externes à l'objet testé (ils appliquent la spécification de l'objet, empêchant tout accès à ses détails d'implémentation et provoquant une erreur de compilation si une telle tentative est détectée ). Mais les propres méthodes internes de l'objet ont toutes un accès complet à chaque détail d'implémentation. La méthode d'auto-test se trouve donc dans une situation unique: elle doit être une méthode interne en raison de sa nature (l'auto-test est évidemment une méthode de l'objet testé), mais elle doit recevoir toute la discipline de compilation d'une méthode externe ( il doit être indépendant des détails d'implémentation de l'objet). Peu ou pas de langages de programmation permettent de discipliner la méthode interne d'un objet comme s'il s'agissait d'une méthode externe. Il s'agit donc d'un problème de conception de langage de programmation important.

En l'absence de prise en charge appropriée du langage de programmation, la meilleure façon de le faire est de créer un objet compagnon. En d'autres termes, pour chaque objet que vous codez (appelons-le "Big_Object"), vous créez également un deuxième objet compagnon dont le nom consiste en un suffixe standard concaténé avec le nom de l'objet "réel" (dans ce cas, "Big_Object_Self_Test "), et dont la spécification consiste en une seule méthode (" Big_Object_Self_Test.Self_Test (This_Big_Object: Big_Object) return Boolean; "). L'objet compagnon dépendra alors de la spécification de l'objet principal, et le compilateur appliquera pleinement toute la discipline de cette spécification par rapport à l'implémentation de l'objet compagnon.

5
commenter8

Ceci est en réponse à un grand nombre de commentaires suggérant que les tests en ligne ne sont pas effectués car il est difficile, voire impossible, de supprimer le code de test des versions. C'est faux. Presque tous les compilateurs et assembleurs le prennent déjà en charge, avec les langages compilés, tels que C, C++, C #, cela se fait avec ce qu'on appelle des directives de compilation.

Dans le cas de c # (je crois aussi que c ++, la syntaxe peut avoir légèrement différent selon le compilateur que vous utilisez), c'est comment vous pouvez le faire.

#define DEBUG //  = true if c++ code
#define TEST /* can also be defined in the make file for c++ or project file for c# and applies to all associated .cs/.cpp files */

//somewhere in your code
#if DEBUG
// debug only code
#Elif TEST
// test only code
#endif

Parce que cela utilise des directives du compilateur, le code n'existera pas dans les fichiers exécutables qui sont créés si les indicateurs ne sont pas définis. C'est aussi ainsi que vous créez des programmes "écrire une fois, compiler deux fois" pour plusieurs plates-formes/matériels.

5
john

Nous utilisons des tests en ligne avec notre code Perl. Il y a un module, Test :: Inline , qui génère des fichiers de test à partir du code en ligne.

Je ne suis pas particulièrement doué pour organiser mes tests, et je les ai trouvés plus faciles et plus susceptibles d'être maintenus lorsqu'ils sont en ligne.

Répondant à quelques-unes des préoccupations soulevées:

  • Les tests en ligne sont écrits dans des sections POD, ils ne font donc pas partie du code réel. Ils sont ignorés par l'interpréteur, il n'y a donc pas de surcharge de code.
  • Nous utilisons pliage Vim pour masquer les sections de test. La seule chose que vous voyez est une seule ligne au-dessus de chaque méthode testée comme +-- 33 lines: #test----. Lorsque vous souhaitez travailler avec le test, il vous suffit de le développer.
  • Le module Test :: Inline "compile" les tests dans des fichiers compatibles TAP normaux, afin qu'ils puissent coexister avec les tests traditionnels.

Pour référence:

2
mla

Une autre raison de séparer les tests est que vous utilisez souvent des bibliothèques supplémentaires ou même différentes pour les tests que pour l'implémentation réelle. Si vous mélangez tests et implémentation, l'utilisation accidentelle des bibliothèques de test dans l'implémentation ne peut pas être détectée par le compilateur.

De plus, les tests ont tendance à avoir beaucoup plus de lignes de code que les parties d'implémentation qu'ils testent, vous aurez donc du mal à trouver l'implémentation entre tous les tests. :-)

1

Erlang 2 prend réellement en charge les tests en ligne. Toute expression booléenne dans le code qui n'est pas utilisée (par exemple assignée à une variable ou passée) est automatiquement traitée comme un test et évaluée par le compilateur; si l'expression est fausse, le code ne se compile pas.

1
Mark Rendle

Ce n'est pas vrai. Il est préférable de placer vos tests unitaires à côté du code de production lorsque le code de production, en particulier lorsque la routine de production est pure.

Si vous développez sous .NET, par exemple, vous pouvez mettre votre code de test dans l'assembly de production, puis utiliser Scalpel pour les supprimer avant l'expédition.

0
zumalifeguard