Test unitaire Catalogue Anti-Patterns

203

anti-modèle : il doit y avoir au moins deux éléments clés présents pour distinguer formellement un anti-modèle réel d'une simple mauvaise habitude, mauvaise pratique ou mauvaise idée:

  • Certains schémas d'action, de processus ou de structure répétés qui semblent initialement bénéfiques, mais qui produisent en fin de compte plus de mauvaises conséquences que des résultats bénéfiques, et
  • Une solution refactorisée clairement documentée, éprouvée dans la pratique et reproductible.

Votez pour l'anti-schéma TDD que vous avez vu "à l'état sauvage" une fois de trop.
Le billet de blog de James Carr et la discussion connexe sur le testdrivendevelopment yahoogroup

Si vous en avez trouvé un «sans nom», postez-les aussi. Un message par anti-modèle pour que les votes comptent pour quelque chose.

Mon intérêt direct est de trouver le sous-ensemble top-n afin que je puisse en discuter dans une réunion déjeuner dans un avenir proche.

Gishu
la source
Aaron, tu sembles être sur tout ça :) Serait-ce une bonne idée d'ajouter les slogans ou les slogans comme commentaires pour que nous ayons moins de défilement .. que dire?
Gishu
1
Ça monte plutôt bien .. merci les gars et les filles. Continuez à venir .. l'un des messages SO les plus informatifs IMHO
Gishu
2
+1 aime ce fil !!! Et la plupart d'entre elles sont si vraies et répandues aussi!
Chii
Joli fil, pourquoi ce wiki communautaire est-il si ???
Quibblesome
2
Parce que c'est une sorte de sondage - vous ne voudriez pas récolter des représentants juste parce que vous avez publié le type d'anti-modèle le plus courant;)
Gishu

Réponses:

70

Citoyens de deuxième classe - le code de test n'est pas aussi bien refactorisé que le code de production, contenant beaucoup de code dupliqué, ce qui rend difficile la maintenance des tests.

Ilja Preuß
la source
67

The Free Ride / Piggyback - James Carr, Tim Ottinger
Plutôt que d'écrire une nouvelle méthode de cas de test pour tester une autre / caractéristique / fonctionnalité distincte , une nouvelle assertion (et ses actions correspondantes, c'est-à-dire les étapes de l'AAA), se déplace dans un cas de test existant .

Aaron Digulla
la source
15
Ouais, c'est ma préférée. Je le fais tout le temps. Oh ... attends ... tu as dit que c'était une mauvaise chose. :-)
guidoism
1
Je ne suis pas sûr que ce soit un anti-modèle. Tous les invariants doivent être trueaprès chaque appel de mutateur possible. Vous voudrez donc vérifier que chaque invariant est trueaprès chaque combinaison de mutateur et de données d'entrée que vous testez. Mais vous souhaiterez réduire la duplication et vous assurer de vérifier tous les invariants, y compris ceux qui ne provoquent pas actuellement d'échecs de test. Vous les mettez donc tous dans une checkInvariants()fonction de vérification et vous les utilisez dans chaque test. Le code change et un autre invariant est ajouté. Vous mettez cela aussi dans la fonction, bien sûr. Mais c'est un freerider.
Raedwald
2
@Raedwald - Au fil du temps, le nom du test ne correspond plus à tout ce qu'il teste. De plus, vous avez des attaques en raison de tests entrelacés; un échec n'indique pas la cause exacte de l'échec. Par exemple, un exemple canonique de ce test lirait quelque chose comme Opaque Superset of all Arrange steps >> Act >> Assert A >> Act some more >> Assert B >> Act some more >> Assert C. Maintenant idéalement si A et C sont cassé, vous devriez voir 2 échecs de test. Avec le test ci-dessus, vous n'en verriez qu'un, puis vous corrigez A et lors de la prochaine exécution, il vous dirait que maintenant C est cassé. imaginez maintenant 5-6 tests distincts fusionnés ..
Gishu
1
"le nom du test ne correspond plus à toutes les choses qu'il teste" uniquement si le test est nommé pour la condition de publication qui était initialement présente. Si vous nommez pour la combinaison nom de méthode, état de configuration et données d'entrée (arguments de méthode), il n'y a pas de problème.
Raedwald
"un échec n'indique pas la cause exacte de l'échec", aucun échec d' assertion n'indique jamais la cause d'un échec. Cela nécessite de fouiller dans les détails de l'implémentation: débogage pour un échec de régression, votre connaissance de l'état de développement pour certains travaux TDD.
Raedwald
64

Chemin heureux

Le test reste sur des chemins heureux (c'est-à-dire les résultats attendus) sans tester les limites et les exceptions.

Antipatterns JUnit

Géoglyphe
la source
Cause: Soit des contraintes de temps exagérées, soit une paresse flagrante. Solution refactorisée: prenez le temps d'écrire plus de tests pour vous débarrasser des faux positifs. Cette dernière cause a besoin d'un fouet. :)
Spoike
59

Le héros local

Un scénario de test qui dépend de quelque chose de spécifique à l'environnement de développement sur lequel il a été écrit pour s'exécuter. Le résultat est que le test réussit sur les boîtes de développement, mais échoue lorsque quelqu'un tente de l'exécuter ailleurs.

La dépendance cachée

Étroitement lié au héros local, un test unitaire qui nécessite que certaines données existantes aient été remplies quelque part avant l'exécution du test. Si ces données n'ont pas été renseignées, le test échouera et ne laissera aucune indication au développeur sur ce qu'il voulait, ou pourquoi ... les obligeant à fouiller dans des hectares de code pour savoir d'où les données qu'il utilisait étaient censées provenir.


Malheureusement, cela a été trop souvent constaté avec les anciens fichiers .dll qui dépendent de fichiers .ini nébuleux et variés qui sont constamment désynchronisés sur un système de production donné, et encore moins qui existent sur votre machine sans consultation approfondie des trois développeurs responsables de ces fichiers. Soupir.

annakata
la source
C'est un bel exemple de l'acronyme du développeur WOMPC. "Fonctionne sur mon PC!" (On dit généralement que les testeurs sont dans le dos.)
Joris Timmermans
58

Gang de chaîne

Quelques tests qui doivent être exécutés dans un certain ordre, c'est-à-dire qu'un test modifie l'état global du système (variables globales, données dans la base de données) et le ou les tests suivants en dépendent.

Vous voyez souvent cela dans les tests de base de données. Au lieu d'effectuer une restauration teardown(), les tests valident leurs modifications dans la base de données. Une autre cause courante est que les modifications de l'état global ne sont pas encapsulées dans des blocs try / finally qui nettoient en cas d'échec du test.

Aaron Digulla
la source
celui-ci est tout simplement méchant .. Brise les tests doivent être notion indépendante. Mais j'ai lu à ce sujet à plusieurs endroits .. suppose que «TDD populaire» est assez foiré
Gishu
56

La moquerie
Parfois, la moquerie peut être bonne et pratique. Mais parfois, les développeurs peuvent se perdre et dans leur effort pour se moquer de ce qui n'est pas testé. Dans ce cas, un test unitaire contient tellement de simulations, de moignons et / ou de contrefaçons que le système testé n'est même pas testé du tout, mais les données renvoyées par les simulations sont ce qui est testé.

Source: poste de James Carr.

Gishu
la source
2
Je crois que la cause en est que votre classe testée a beaucoup trop de dépendances. L'alternative refactorisée consiste à extraire le code qui peut être isolé.
Spoike
@Spoike; Si vous êtes dans une architecture en couches, cela dépend vraiment du rôle de la classe; certaines couches ont tendance à avoir plus de dépendances que d'autres.
krosenvold
J'ai vu récemment, dans un blog respecté, la création d'une configuration d'entité fictive à renvoyer à partir d'un référentiel fictif. WTF? Pourquoi ne pas simplement instancier une entité réelle en premier lieu. Moi-même, je viens d'être brûlé par une interface simulée où mon implémentation lançait des exceptions NotImplemented tout autour.
Thomas Eyde
40

Le Silent Catcher - Kelly?
Un test qui réussit si une exception est levée .. même si l'exception qui se produit réellement est une exception différente de celle prévue par le développeur.
Voir aussi: Secret Catcher

[Test]
[ExpectedException(typeof(Exception))]
public void ItShouldThrowDivideByZeroException()
{
   // some code that throws another exception yet passes the test
}
Gishu
la source
Celui-ci est délicat et dangereux (c'est-à-dire vous fait penser que vous avez testé du code qui explose toujours à chaque exécution). C'est pourquoi j'essaie d'être précis à la fois sur une classe d'exception et sur quelque chose d'unique dans le message.
Joshua Cheek
34

L'inspecteur
Un test unitaire qui viole l'encapsulation dans un effort pour atteindre une couverture de code à 100%, mais en sait tellement sur ce qui se passe dans l'objet que toute tentative de refactorisation cassera le test existant et exigera que tout changement soit reflété dans l'unité tester.


'comment puis-je tester mes variables membres sans les rendre publiques ... juste pour des tests unitaires?'

Gishu
la source
2
Cause: dépendance absurde aux tests en boîte blanche. Il existe des outils pour générer ce type de tests comme Pex sur .NET. Solution refactorisée: testez plutôt le comportement et si vous avez vraiment besoin de vérifier les valeurs limites, laissez les outils automatisés générer le reste.
Spoike
1
Avant l'arrivée de Moq, j'ai dû abandonner les frameworks de moquerie au profit de l'écriture manuscrite de mes simulacres. Il était tout simplement trop facile de lier mes tests à la mise en œuvre réelle, ce qui rend presque impossible toute refactorisation. Je ne peux pas faire la différence, sauf avec Moq, je fais rarement ce genre d'erreurs.
Thomas Eyde
34

Configuration excessive - James Carr
Un test qui nécessite une configuration énorme pour même commencer les tests. Parfois, plusieurs centaines de lignes de code sont utilisées pour préparer l'environnement à un test, avec plusieurs objets impliqués, ce qui peut rendre difficile de vraiment déterminer ce qui est testé en raison du «bruit» de toute la configuration en cours. (Src: poste de James Carr )

Gishu
la source
Je comprends qu'une configuration de test excessive indique généralement a) un code mal structuré ou b) une moquerie insuffisante, n'est-ce pas?
Topher Hunt
Eh bien, chaque situation pourrait être différente. Cela pourrait être dû à un couplage élevé. Mais il s'agit généralement d'un cas de sur-spécification, spécifiant (simulacre d'attentes) chaque collaborateur dans le scénario - cela couple le test à la mise en œuvre et les rend fragiles. Si l'appel au collaborateur est un détail accessoire au test, il ne doit pas figurer dans le test. Cela aide également à garder le test court et lisible.
Gishu
32

Sonde anale

Un test qui doit utiliser des moyens insensés, illégaux ou autrement malsains pour effectuer sa tâche comme: lire des champs privés en utilisant setAccessible de Java (vrai) ou étendre une classe pour accéder à des champs / méthodes protégés ou avoir à mettre le test dans un certain package pour accéder empaqueter les champs / méthodes globaux.

Si vous voyez ce modèle, les classes testées utilisent trop de données masquées.

La différence entre cela et l'inspecteur est que la classe sous test essaie de cacher même les choses dont vous avez besoin pour tester. Votre objectif n'est donc pas d'atteindre une couverture de test à 100%, mais de pouvoir tout tester. Pensez à une classe qui n'a que des champs privés, une run()méthode sans arguments et sans getters du tout. Il n'y a aucun moyen de tester cela sans enfreindre les règles.


Commentaire de Michael Borgwardt: Ce n'est pas vraiment un contre-modèle de test, c'est du pragmatisme pour faire face aux déficiences du code testé. Bien sûr, il est préférable de corriger ces lacunes, mais cela peut ne pas être possible dans le cas des bibliothèques tierces.

Aaron Digulla: Je suis en quelque sorte d'accord. Peut-être que cette entrée est vraiment mieux adaptée à un wiki "JUnit HOWTO" et non à un antipattern. Commentaires?

Aaron Digulla
la source
n'est-ce pas la même chose que l'inspecteur?
Gishu
1
Hmm .. cette ligne «la classe sous test essaie de cacher même les choses que vous devez tester» indique une lutte de pouvoir entre la classe et le test. S'il doit être testé .. il devrait être accessible au public d'une manière ou d'une autre .. via le comportement / l'interface de la classe .. cela sent en quelque sorte une violation de l'encapsulation
Gishu
2
npellow: Maven2 a un plugin pour ça, non?
Aaron Digulla
1
Ce n'est pas vraiment un test anti-modèle, c'est du pragmatisme pour faire face aux déficiences du code testé. Bien sûr, il est préférable de corriger ces lacunes, mais cela peut ne pas être possible dans le cas des bibliothèques tierces.
Michael Borgwardt
1
IDK, cela doit avoir une sorte d'effet secondaire. Je testerais l'effet secondaire. Je ne sais pas ce que vous voulez dire sur le test de l'API tierce, je dirais que vous devriez envelopper dans votre propre code que vous pouvez tester a été utilisé correctement, puis tester l'intégration de ce code par rapport à l'API tierce. Ne testerait pas à l'unité le code tiers.
Joshua Cheek
26

Le test sans nom - Nick Pellow

Le test qui est ajouté pour reproduire un bogue spécifique dans le traqueur de bogues et dont l'auteur pense qu'il ne garantit pas son propre nom. Au lieu d'améliorer un test manquant existant, un nouveau test est créé appelé testForBUG123.

Deux ans plus tard, lorsque ce test échoue, vous devrez peut-être d'abord essayer de trouver BUG-123 dans votre suivi des bogues pour comprendre l'intention du test.

npellow
la source
7
Tellement vrai. Tho qui est légèrement plus utile qu'un test appelé "TestMethod"
NikolaiDante
8
à moins que le bugtracker ne change, et que vous perdiez l'ancien tracker et ses identifiants de problème ... donc PROJECT-123 ne veut plus rien dire ...
Chii
25

The Slow Poke

Un test unitaire incroyablement lent. Lorsque les développeurs démarrent, ils ont le temps d'aller aux toilettes, de fumer, ou pire, de lancer le test avant de rentrer chez eux à la fin de la journée. (Src: poste de James Carr )

alias les tests qui ne seront pas exécutés aussi souvent qu'ils le devraient

Gishu
la source
Certains tests s'exécutent lentement par leur nature même. Si vous décidez de ne pas les exécuter aussi souvent que les autres, assurez-vous qu'ils s'exécutent au moins sur un serveur CI aussi souvent que possible.
Chris Vest
C'est une question évidente, mais quels sont les moyens les plus généraux de résoudre ce problème?
Topher Hunt
Cela semble initialement bénéfique, hein?
Kev
1
@TopherHunt Généralement, les tests sont lents car ils ont une dépendance coûteuse (c'est-à-dire système de fichiers, base de données). L'astuce consiste à analyser les dépendances jusqu'à ce que vous voyiez le problème, puis à pousser la dépendance vers le haut de la pile d'appels. J'ai écrit une étude de cas où mes élèves ont pris leur suite de tests unitaires de 77 secondes à 0,01 seconde en corrigeant leurs dépendances: github.com/JoshCheek/fast_tests
Joshua Cheek
20

Le papillon

Vous devez tester quelque chose qui contient des données qui changent tout le temps, comme une structure qui contient la date actuelle, et il n'y a aucun moyen de clouer le résultat à une valeur fixe. La partie laide est que vous ne vous souciez pas du tout de cette valeur. Cela rend simplement votre test plus compliqué sans ajouter de valeur.

La chauve-souris de son aile peut provoquer un ouragan à l'autre bout du monde. - Edward Lorenz, L'effet papillon

Aaron Digulla
la source
Quel est l'anti-pattern ici: à quoi ressemble un test comme celui-ci? Y a-t-il une solution? Y a-t-il un avantage discutable au code sous test pour tenir compte d'une dépendance comme System.DateTime.Now, en plus d'avoir des tests unitaires plus simples ou plus déterministes?
Merlyn Morgan-Graham
1
En Java, un exemple serait d'appeler toString()un objet qui n'écrase pas la méthode. Cela vous donnera l'ID de l'objet qui dépend de l'adresse mémoire. Ou toString()contient la clé primaire de l'objet et qui change à chaque fois que vous exécutez le test. Il existe trois façons de résoudre ce problème: 1. Modifiez le code que vous testez, 2. utilisez regexp pour supprimer les parties variables des résultats du test ou 3. utilisez des outils puissants pour écraser les services système pour leur faire renvoyer des résultats prévisibles.
Aaron Digulla
La cause sous-jacente de cet anti-modèle est que le code testé ne se soucie pas de l'effort que cela pourrait être pour le tester. Le caprice d'un développeur est donc l'aile du papillon qui cause des problèmes ailleurs.
Aaron Digulla
19

Le test du scintillement (Source: Romilly Cocking)

Un test qui échoue juste occasionnellement, pas à des moments précis, et est généralement dû aux conditions de course dans le test. Se produit généralement lors du test d'un élément asynchrone, tel que JMS.

Peut-être un super ensemble de l' anti-modèle " Wait and See " et " The Sleeper ".

La construction a échoué, eh bien, exécutez à nouveau la construction. - Développeur anonyme

Stuart
la source
@Stuart - une vidéo incontournable décrivant ceci est "Car Stalled - Try Now!" videosift.com/video/… Ce modèle pourrait également être appelé "Essayez maintenant!", ou tout simplement - "Le test Flakey"
npellow
1
J'ai écrit une fois un test pour un PRGN qui a assuré une distribution correcte. Parfois, il échouait au hasard. Allez comprendre. :-)
Chris Vest
1
Ne serait-ce pas un bon test à avoir? Si un test échoue, vous devez rechercher la source du problème. Je me suis battu avec quelqu'un au sujet d'un test qui a échoué entre 9 heures et minuit. Il a dit que c'était aléatoire / intermittent. Il a finalement été attribué à un bug concernant les fuseaux horaires. Allez comprendre.
Trenton
@Christian Vest Hansen: ne pourriez-vous pas l'ensemencer?
Andrew Grimm
@trenton Ce n'est qu'un bon test à avoir si les développeurs peuvent être dérangés pour le retrouver, au lieu de simplement l'ignorer (avec lequel ils peuvent s'en tirer, car cela passe la plupart du temps).
Will Sheppard
19

Attend et regarde

Un test qui exécute un code de configuration et doit ensuite «attendre» un certain temps avant de pouvoir «voir» si le code testé fonctionne comme prévu. Un testMethod qui utilise Thread.sleep () ou équivalent est très certainement un test "Wait and See".

En règle générale, vous pouvez voir cela si le test teste du code qui génère un événement externe au système tel qu'un e-mail, une requête http ou écrit un fichier sur le disque.

Un tel test peut également être un héros local car il échouera lorsqu'il sera exécuté sur une boîte plus lente ou un serveur CI surchargé.

L'anti-modèle Wait and See ne doit pas être confondu avec The Sleeper .

npellow
la source
Hmm .. eh bien j'utilise quelque chose comme ça. sinon, comment pourrais-je tester du code multithread?
Gishu
@ Gishu, voulez-vous vraiment tester plusieurs threads en cours d'exécution simultanément? J'essaie simplement de tester unitairement tout ce que la méthode run () fait de manière isolée. Un moyen facile de le faire est d'appeler run () - qui bloquera, au lieu de start (), du test unitaire.
npellow
@Gishu utilise CountDownLatches, Semaphores, Conditions ou similaires, pour que les threads se disent quand ils peuvent passer au niveau suivant.
Chris Vest
Un exemple: madcoderspeak.blogspot.com/2008/11/… Brew button evt. L'observateur interroge à intervalles réguliers et déclenche des événements modifiés. Dans ce cas, j'ajoute un délai afin que les threads d'interrogation puissent s'exécuter avant la fin du test.
Gishu
Je pense que le lien du dessin animé est rompu.
Andrew Grimm
17

Appareil partagé de manière inappropriée - Tim Ottinger
Plusieurs cas de test dans l'appareil de test n'utilisent même pas ou n'ont pas besoin d'être configurés / démontés. En partie en raison de l'inertie du développeur pour créer un nouveau montage de test ... plus facile d'ajouter simplement un cas de test supplémentaire à la pile

Gishu
la source
1
Il se peut également que la classe testée essaie d'en faire trop.
Mike Two du
16

Le géant

Un test unitaire qui, bien qu'il teste valablement l'objet testé, peut s'étendre sur des milliers de lignes et contenir de nombreux cas de test. Cela peut être un indicateur que le système testé est un objet divin (article de James Carr).

Un signe certain pour celui-ci est un test qui s'étend sur plus de quelques lignes de code. Souvent, le test est si compliqué qu'il commence à contenir des bogues de son propre comportement ou floconneux.

Gishu
la source
15

J'y croirai quand je verrai des interfaces graphiques clignotantes
Une fixation / obsession malsaine pour tester l'application via son interface graphique `` comme un vrai utilisateur ''

Tester les règles métier via l'interface graphique est une forme terrible de couplage. Si vous écrivez des milliers de tests via l'interface graphique, puis modifiez votre interface graphique, des milliers de tests sont interrompus.
Au lieu de cela, testez uniquement les éléments de l'interface graphique via l'interface graphique et associez l'interface graphique à un système factice au lieu du système réel, lorsque vous exécutez ces tests. Testez les règles métier via une API qui n'implique pas l'interface graphique. - Bob Martin

"Vous devez comprendre que voir, c'est croire, mais aussi savoir que croire, c'est voir." - Denis Waitley

Gishu
la source
1
Si vous pensiez que flasher les interfaces graphiques était faux, j'ai vu quelqu'un qui a écrit un test jUnit qui a démarré l'interface graphique et a eu besoin d'une interaction utilisateur pour continuer. Il a suspendu le reste de la suite de tests. Voilà pour l'automatisation des tests!
Spoike
Je ne suis pas d'accord. Les tests de GUI sont difficiles, mais ils sont également une source d'erreurs. Ne pas les tester est juste paresseux.
Ray
3
le point ici est que vous ne devriez pas tester les interfaces graphiques mais plutôt que vous ne devriez pas tester uniquement via l'interface graphique. Vous pouvez effectuer des tests «sans tête» sans l'interface graphique. Gardez l'interface graphique aussi fine que possible - utilisez une saveur de MVP - vous pouvez alors vous en passer sans le tester du tout. Si vous constatez que des bogues apparaissent tout le temps dans la fine couche GUI, couvrez-les avec des tests .. mais la plupart du temps, je ne trouve pas que cela en vaille la peine. Les erreurs de «câblage» de l'interface graphique sont généralement plus faciles à corriger ...
Gishu
@Spoike: les tests manuels guidés ne sont pas mauvais, ni l'utilisation de jUnit (ou tout autre framework de tests unitaires) pour piloter des tests automatisés qui ne sont pas des tests unitaires. Vous ne devriez tout simplement pas mettre ceux-ci dans le même projet, ni les traiter comme des tests unitaires (par exemple exécutés en permanence, ou après chaque build).
Merlyn Morgan-Graham
1
@ MerlynMorgan-Graham Je suis d'accord, et je ne voulais pas dire que vous ne devriez pas tester l'interface graphique. La conviction des membres de l'équipe selon laquelle il était acceptable de mélanger des tests manuels guidés avec des tests automatiques, me perturbait. J'ai découvert plus tard que c'était un excellent moyen d'amener tous ceux qui n'étaient pas habitués au TDD à cesser de l'utiliser. Je trouve que mélanger des tests fonctionnels (qui sont volatils) avec des tests unitaires (qui sont censés être stables) est mauvais si vous voulez suivre le processus TDD.
Spoike
14

Le dormeur, alias Mont Vésuve - Nick Pellow

Un test qui est destiné à échouer à une heure et une date spécifiques à l'avenir. Cela est souvent dû à une vérification des limites incorrecte lors du test du code qui utilise un objet Date ou Calendrier. Parfois, le test peut échouer s'il est exécuté à une heure très précise de la journée, telle que minuit.

«The Sleeper» ne doit pas être confondu avec l' anti-modèle « Wait And See ».

Ce code aura été remplacé bien avant l'an 2000 - De nombreux développeurs en 1960

npellow
la source
Je préfère appeler cela un volcan dormant :) .. mais je sais de quoi vous parlez .. par exemple une date choisie comme date future pour un test au moment de la rédaction deviendra une date présente / passée lorsque cette date passe par là. Pourriez-vous poster un exemple .. juste pour illustrer cela.
Gishu
@Gishu - +1. Je pensais la même chose, mais je ne pouvais pas choisir entre les deux. J'ai mis à jour le titre pour le rendre un peu plus clair;)
npellow
11

L'arbre mort

Un test où un stub a été créé, mais le test n'a pas été réellement écrit.

J'ai vu cela dans notre code de production:

class TD_SomeClass {
  public void testAdd() {
    assertEquals(1+1, 2);
  }
}

Je ne sais même pas quoi en penser.

Révérend Gonzo
la source
8
:) - également connu sous le nom de Backdoor de conformité de processus.
Gishu
1
Nous en avons eu un exemple récemment dans un test et une méthode en cours de test qui avaient été refactorisés à plusieurs reprises. Après quelques itérations, le test est devenu un appel à la méthode en cours de test. Et parce que la méthode est maintenant revenue nulle, il n'y avait aucune assertion à affirmer. Donc, fondamentalement, le test s'assurait simplement que la méthode ne levait pas d'exception. Peu importait que cela fasse quelque chose d'utile ou correctement. Je l'ai trouvé dans la révision du code et j'ai demandé: "Alors ... qu'est-ce qu'on teste même ici?"
2015
11

a mordu par cela aujourd'hui:

Sol humide :
le test crée des données persistantes quelque part, mais le test ne se nettoie pas une fois terminé. Cela entraîne l'échec des tests (le même test ou éventuellement d'autres tests) lors des exécutions de test suivantes .

Dans notre cas, le test a laissé un fichier dans le répertoire "temp", avec les autorisations de l'utilisateur qui a exécuté le test la première fois. Lorsqu'un utilisateur différent a essayé de tester sur la même machine: boom. Dans les commentaires sur le site de James Carr, Joakim Ohlrogge a appelé cela le "Sloppy Worker", et cela faisait partie de l'inspiration pour "Generous Leftovers". J'aime mieux mon nom (moins insultant, plus familier).

Zac Thompson
la source
Vous pouvez utiliser la règle de dossier temporaire de junit pour éviter les sols humides.
DaveFar
Ce type se rapporte à un modèle anti-intégration continue. Dans CI, chaque développeur doit avoir son propre espace de travail et ses propres ressources, et la machine de génération doit également être son propre environnement. Ensuite, vous évitez des problèmes tels que les autorisations (ou peut-être que vous
finissez
11

Le coucou - Frank Carver
Un test unitaire qui se trouve dans un scénario de test avec plusieurs autres, et bénéficie du même processus de configuration (potentiellement long) que les autres tests du scénario de test, mais élimine ensuite tout ou partie des artefacts de la configuration et crée le sien.
Symptôme avancé de: Appareil mal partagé

Gishu
la source
10

The Secret Catcher - Frank Carver
Un test qui à première vue semble ne faire aucun test, en raison de l'absence d'assertions. Mais "Le diable est dans les détails" .. le test repose vraiment sur une exception à lever et attend du framework de test qu'il capture l'exception et la signale à l'utilisateur comme un échec.

[Test]
public void ShouldNotThrow()
{
   DoSomethingThatShouldNotThrowAnException();
}
Gishu
la source
2
Cela peut en fait être un test valide, à mon avis - surtout comme test de régression.
Ilja Preuß
désolé encore une fois, cela a été confondu avec Silent catcher ... les tests unitaires devraient indiquer clairement l'intention de ce qui est testé plutôt que de dire "cela devrait fonctionner" .. (+1 tp quelque chose est mieux que rien. surtout si vous êtes en régression héritée pays)
Gishu
1
Dans ce genre de tests, j'attrape au moins une exception et je l'assigne à une variable. Ensuite, j'affirme pour non nul.
Thomas Eyde
4
Certains cadres ont une Assert.DoesNotThrow(SomeDelegateType act)assertion de style qui peut être utilisée spécifiquement dans des cas comme celui-ci. Je trouve cela moins grossier que d'avoir un cas de test qui réussit lorsqu'un constructeur retourne non nul, mais échoue lorsque le constructeur lance. Un constructeur ne retournera jamais null. (Remarque: s'applique uniquement aux langues où un constructeur est garanti de retourner non nul)
Merlyn Morgan-Graham
10

Le vandalisme environnemental

Un test «unitaire» qui pour diverses «exigences» commence à se répandre dans son environnement, en utilisant et en définissant des variables / ports d'environnement. L'exécution simultanée de deux de ces tests entraînera des exceptions de «port indisponible», etc.

Ces tests seront intermittents et laisseront les développeurs dire des choses comme «il suffit de le relancer».

Une solution que j'ai vue consiste à sélectionner au hasard un numéro de port à utiliser. Cela réduit la possibilité d'un conflit, mais ne résout clairement pas le problème. Donc, si vous le pouvez, moquez toujours le code afin qu'il n'alloue pas réellement la ressource non partageable.

gcrain
la source
@gcrain .. les tests doivent être déterministes. OMI, une meilleure approche serait d'utiliser un port `` bien connu de l'équipe '' pour tester et nettoyer correctement avant et après le test de sorte qu'il soit toujours disponible ...
Gishu
1
@gishu - le problème n'est pas qu'il n'y a pas de méthodes setup () et teardown () pour gérer l'utilisation de ces ports. le problème est par exemple d'exécuter un serveur CI, et plusieurs versions du test s'exécutent en même temps, en essayant d'utiliser les mêmes numéros de port
codés en
10

Le test de Turing

Un testcase généré automatiquement par un outil coûteux qui contient de nombreuses assertions glanées dans la classe testée en utilisant une analyse de flux de données trop intelligente par moitié. Endort les développeurs dans un faux sentiment de confiance que leur code est bien testé, les déchargeant de la responsabilité de concevoir et de maintenir des tests de haute qualité. Si la machine peut écrire les tests pour vous, pourquoi ne peut-elle pas retirer son doigt et écrire l'application elle-même!

Bonjour stupide. - L'ordinateur le plus intelligent du monde à un nouvel apprenti (d'une vieille bande dessinée d'Amiga).

bhumphreys
la source
10

Le test du pôle de quarante pieds

Peur de se rapprocher trop de la classe qu'ils tentent de tester, ces tests agissent à distance, séparés par d'innombrables couches d'abstraction et des milliers de lignes de code de la logique qu'ils vérifient. En tant que tels, ils sont extrêmement fragiles et sensibles à toutes sortes d'effets secondaires qui se produisent lors du voyage épique vers et depuis la classe d'intérêt.

bhumphreys
la source
9

Doppelgänger

Pour tester quelque chose, vous devez copier des parties du code sous test dans une nouvelle classe avec le même nom et le même package et vous devez utiliser la magie du chemin de classe ou un chargeur de classe personnalisé pour vous assurer qu'il est visible en premier (afin que votre copie soit choisie vers le haut).

Ce modèle indique une quantité malsaine de dépendances cachées que vous ne pouvez pas contrôler à partir d'un test.

J'ai regardé son visage ... mon visage! C'était comme un miroir mais ça a fait geler mon sang.

Aaron Digulla
la source
7

La mère poule - Frank Carver
Une configuration commune qui fait bien plus que les cas de test réels ont besoin. Par exemple, la création de toutes sortes de structures de données complexes peuplées de valeurs apparemment importantes et uniques lorsque les tests n'affirment que la présence ou l'absence de quelque chose.
Symptôme avancé de: Appareil mal partagé

Je ne sais pas ce que ça fait ... Je l'ajoute quand même, juste au cas où. - Développeur anonyme

Gishu
la source
7

Le test tout

Je ne peux pas croire que cela n'ait pas été mentionné jusqu'à présent, mais les tests ne devraient pas briser le principe de responsabilité unique .

Je l'ai rencontré tellement de fois, les tests qui enfreignent cette règle sont par définition un cauchemar à maintenir.

thegreendroid
la source
6

Frappeur de ligne

À première vue, les tests couvrent tout et les outils de couverture de code le confirment à 100%, mais en réalité, les tests ne frappent que le code sans aucune analyse de sortie.

couverture-vs-code-accessible

Ruslan Dzhabbarov
la source