Les tests unitaires ne devraient-ils pas utiliser mes propres méthodes?

83

Aujourd'hui, je regardais une vidéo intitulée " JUnit Basics" et l'auteur m'a dit que, lors du test d'une méthode donnée dans votre programme, vous ne devriez pas utiliser d'autres méthodes que vous-même.

Pour être plus précis, il parlait de tester une méthode de création d’enregistrement qui prenait un nom et un nom de famille pour les arguments, et l’utilisait pour créer des enregistrements dans une table donnée. Mais il a affirmé qu'en testant cette méthode, il ne devrait pas utiliser ses autres méthodes DAO pour interroger la base de données afin de vérifier le résultat final (pour vérifier que l'enregistrement a bien été créé avec les bonnes données). Il a affirmé que pour cela, il devrait écrire du code JDBC supplémentaire pour interroger la base de données et vérifier le résultat.

Je pense comprendre l’esprit de son affirmation: vous ne voulez pas que le cas test d’une méthode dépende de l’exactitude des autres méthodes (dans ce cas, la méthode DAO), et ceci est accompli en écrivant (à nouveau) votre propre validation. / code de support (qui devrait être plus spécifique et plus ciblé, donc plus simple).

Néanmoins, des voix dans ma tête ont commencé à protester avec des arguments tels que la duplication de code, des efforts supplémentaires inutiles, etc. Je veux dire, si nous exécutons toute la batterie de tests et que nous testons minutieusement toutes nos méthodes publiques (y compris la méthode DAO), Ne serait-il pas acceptable d’utiliser certaines de ces méthodes tout en testant d’autres méthodes? Si l'un d'entre eux ne fait pas ce qu'il est censé faire, son propre cas de test échouera et nous pourrons le réparer et exécuter à nouveau la batterie de test. Pas besoin de dupliquer le code (même si le code en double est un peu plus simple) ni d'efforts inutiles.

Je suis convaincu par plusieurs applications récentes Excel - VBA que j'ai écrites (testées correctement par Rubberduck pour VBA ), dans lesquelles l'application de cette recommandation impliquerait beaucoup de travail supplémentaire, sans avantage perçu.

Pouvez-vous s'il vous plaît partager vos idées à ce sujet?

Carlossierra
la source
79
C'est un peu étrange de voir un test unitaire impliquant la base de données
Richard Tingle
4
OMI c'est bien d'appeler les autres classes si elles sont assez rapides. Mock tout ce qui doit aller sur le disque ou sur un réseau. Il est inutile de se moquer d'une simple classe IMO.
RubberDuck
2
Vous avez un lien vers cette vidéo?
candied_orange
17
" correctement testé par l'unité grâce à Rubberduck pour VBA " - vous Monsieur, venez de faire ma journée. Je corrigerais la faute de frappe et la modifierais de "RubberDuck" à "Rubberduck", mais je me sentirais comme un spammeur faisant cela en ajoutant un lien vers rubberduckvba.com (je possède le nom de domaine et le copropriétaire du projet avec @RubberDuck) - je vais donc simplement commenter ici. Quoi qu'il en soit, c'est génial de voir des gens utiliser l'outil qui a été responsable de la plupart de mes nuits sans sommeil pendant la plus grande partie des deux dernières années! =)
Mathieu Guindon
4
@ Mat'sMug et RubberDuck J'aime ce que vous faites avec Rubberduck. S'il vous plaît continuez comme ça. Ma vie est certainement plus facile à cause de cela (je réalise beaucoup de petits programmes et de prototypes dans Excel VBA). BTW, la mention Rubberduck dans mon post était juste moi essayant d'être gentil avec RubberDuck lui-même, qui à son tour a été gentil avec moi ici à PE. :)
carlossierra

Réponses:

186

L'esprit de sa demande est en effet correct. Le but des tests unitaires est d’isoler le code, de le tester sans aucune dépendance, afin que tout comportement erroné puisse être rapidement reconnu s’il se produit.

Cela dit, les tests unitaires sont un outil, et il est destiné à servir vos objectifs, ce n'est pas un autel à prier. Parfois, cela signifie laisser des dépendances car elles fonctionnent de manière suffisamment fiable et que vous ne voulez pas vous moquer d'eux, parfois cela signifie que certains de vos tests unitaires sont en fait assez proches, voire réellement, de tests d'intégration.

En fin de compte, vous n'obtenez pas de notes, ce qui est important, c'est le produit final du logiciel testé, mais vous devez juste être conscient du fait que vous enfreignez les règles et que vous décidez quand les compromis en valent la peine.

comment s'appelle-t-il
la source
117
Hourra pour le pragmatisme.
Robert Harvey
6
J'ajouterais que ce n'est pas tant la fiabilité qui est en cause que la vitesse qui conduit à la suppression des dépendances. Le test en mantra d'isolement est tellement convaincant que cet argument est souvent oublié.
Michael Durrant
4
"... les tests unitaires sont un outil, et il est destiné à servir vos objectifs, ce n'est pas un autel à prier." - ça!
Wayne Conrad
15
"Ce n'est pas un autel à prier" - les coachs en colère de la TDD arrivent!
Den
5
@ jpmc26: pire encore, vous ne voulez pas que tous vos tests unitaires réussissent parce qu'ils gèrent correctement le service Web en panne, ce qui est un comportement correct et correct, mais n'exerce jamais le code pour le moment où il est opérationnel! Une fois que vous avez stubé, vous devez choisir entre "up" ou "down" et tester les deux.
Steve Jessop
36

Je pense que cela revient à la terminologie. Pour beaucoup, un "test unitaire" est une chose très spécifique et, par définition, ne peut pas avoir une condition succès / échec qui dépend de tout code en dehors de l' unité (méthode, fonction, etc.) testée. Cela inclurait une interaction avec une base de données.

Pour d'autres, le terme "test unitaire" est beaucoup plus souple et englobe tout type de test automatisé, y compris le code de test qui teste des parties intégrées de l'application.

Un puriste de test (si je peux utiliser ce terme) pourrait appeler cela un test d'intégration , à distinguer d'un test unitaire sur la base que le test dépend de plus d'une unité de code pure .

Je suppose que vous utilisez la version plus souple du terme "test unitaire" et que vous faites en réalité référence à un "test d'intégration".

En utilisant ces définitions, vous ne devriez pas dépendre de code en dehors de l'unité testée dans un "test unitaire". Dans un "test d'intégration", cependant, il est parfaitement raisonnable d'écrire un test qui teste un morceau de code spécifique, puis inspecte la base de données pour vérifier qu'elle satisfait aux critères de réussite.

Que vous deviez dépendre de tests d'intégration ou de tests unitaires, ou des deux, est un sujet de discussion beaucoup plus vaste.

Eric King
la source
Vous avez raison sur vos soupçons. J'utilise mal les termes et je peux maintenant voir comment chaque type de test peut être traité de manière plus appropriée. Merci!
carlossierra
11
"Pour d'autres, le terme" test unitaire "est beaucoup plus lâche" - mis à part quoi, les "frameworks de test unitaire" constituent un moyen très pratique d'organiser et d'exécuter des tests de niveau supérieur ;-)
Steve Jessop
1
Plutôt qu'une distinction "pure" vs "en vrac", chargée de connotations, je suggérerais que la distinction soit faite entre ceux qui voient les "unités", les "dépendances", etc. du point de vue du domaine (par exemple, "l'authentification" compterait en tant qu'unité, "base de données" compterait comme une dépendance, etc.) par rapport à ceux qui les voient du point de vue d'un langage de programmation (par exemple, les méthodes sont des unités, les classes sont des dépendances). Je trouve que définir ce que l'on entend par des expressions telles que "l'unité sous test" peut transformer des arguments ("Vous avez tort!") En discussions ("Est-ce le bon niveau de granularité?").
Warbo
1
@EricKing Bien sûr, pour la très grande majorité de ceux de votre première catégorie, l' idée même d'impliquer la base de données dans tous les tests unitaires est anathème, que vous utilisiez vos DAO ou que vous utilisiez directement la base de données.
Periata Breatta
1
@AmaniKilumanga La question était fondamentalement "quelqu'un a dit que je ne devrais pas faire cette chose dans les tests unitaires mais je le fais dans les tests unitaires et je pense que ça va. Est-ce que ça va?" Et ma réponse a été "Oui, mais la plupart des gens appelleraient cela un test d'intégration au lieu d'un test unitaire.". En ce qui concerne votre question, je ne sais pas ce que vous entendez par "les tests d'intégration peuvent-ils utiliser des méthodes de code de production?".
Eric King
7

La réponse est oui et non...

Les tests uniques qui fonctionnent de manière isolée sont un impératif absolu, car ils vous permettent de déterminer le fonctionnement fondamental du code de bas niveau. Mais en codant de plus grandes bibliothèques, vous trouverez également des zones de code qui nécessiteront des tests couvrant plusieurs unités.

Ces tests inter-unités sont utiles pour la couverture de code et lors du test de la fonctionnalité de bout en bout, mais ils présentent quelques inconvénients dont vous devez être conscient:

  • Sans test isolé pour confirmer ce qui ne fonctionne pas, un test "inter-unités" échoué nécessitera un dépannage supplémentaire pour déterminer ce qui ne va pas avec votre code.
  • Compter trop sur les tests inter-unités peut vous faire sortir de l’esprit de contrat dans lequel vous devriez toujours être dans l’écriture de code SOLID orienté objet. Les tests isolés ont généralement du sens, car vos unités ne devraient effectuer qu’une seule action élémentaire.
  • Les tests de bout en bout sont souhaitables, mais ils peuvent s'avérer dangereux si vous devez écrire dans une base de données ou effectuer une action que vous ne voudriez pas effectuer dans un environnement de production. C'est l'une des nombreuses raisons pour lesquelles les frameworks moqueurs tels que Mockito sont si populaires parce qu'ils vous permettent de simuler un objet et d'imiter un test de bout en bout sans modifier réellement quelque chose que vous ne devriez pas.

À la fin de la journée, vous voulez avoir les deux… beaucoup de tests qui piègent les fonctionnalités de bas niveau et quelques-uns qui testent les fonctionnalités de bout en bout. Concentrez-vous sur la raison pour laquelle vous écrivez des tests en premier lieu. Ils sont là pour vous donner l'assurance que votre code fonctionne comme vous le souhaitez. Tout ce que vous devez faire pour y arriver est très bien.

Le fait triste mais vrai est que si vous utilisez des tests automatisés, vous avez déjà une longueur d'avance sur de nombreux développeurs. Les bonnes pratiques de test sont la première chose à faire lorsqu'une équipe de développement est confrontée à des délais serrés. Donc, tant que vous vous en tenez à vos armes et que vous écrivez des tests unitaires reflétant de manière significative les performances de votre code, je ne suis pas obsédé par la pureté de vos tests.

Humide
la source
4

Un problème lié à l'utilisation d'autres méthodes d'objet pour tester une condition est que vous allez manquer des erreurs qui s'annulent. Plus important encore, vous passez à côté de la douleur d'une implémentation de test difficile, et cette douleur vous apprend quelque chose sur votre code sous-jacent. Les tests douloureux signifient maintenant un entretien douloureux plus tard.

Réduisez la taille de vos unités, divisez votre classe, votre refactorisation et votre nouvelle conception jusqu'à ce qu'il soit plus facile de tester sans réimplémenter le reste de votre objet. Obtenez de l'aide si vous pensez que votre code ou vos tests ne peuvent plus être simplifiés. Essayez de penser à un collègue qui semble toujours avoir de la chance et obtenez des missions faciles à tester proprement. Demandez-lui de l'aide, car ce n'est pas de la chance.

Karl Bielefeldt
la source
1
La clé est ici dans l'unité. Si vous devez appeler d'autres procédures et processus, il ne s'agira pas d'une unité. Si plusieurs appels sont passés et que plusieurs étapes doivent être validées, alors s'il s'agit d'un gros morceau de code par rapport à une unité, divisez-le en morceaux plus petits couvrant un seul élément de logique. Normalement, un appel à une base de données serait également simulé ou un DB en mémoire serait utilisé dans le test unitaire par rapport à l'accès à une base de données réelle et nécessitant l'annulation des données après le test.
dlb
1

Si l'unité de fonctionnalité testée est «les données sont-elles stockées de manière persistante et récupérables», je ferais alors en sorte que le test unitaire teste - stocker dans une base de données réelle, détruire tout objet contenant des références à la base de données, puis appeler le code pour extraire le objets en arrière.

Tester si un enregistrement est créé dans la base de données semble concerner les détails de la mise en œuvre du mécanisme de stockage plutôt que de tester l'unité de fonctionnalité exposée au reste du système.

Vous voudrez peut-être raccourcir le test pour améliorer les performances en utilisant une base de données fictive, mais des sous-traitants ont fait exactement cela et sont partis à la fin de leur contrat avec un système qui a réussi ces tests, mais qui n'a en réalité rien stocké dans la base de données. entre les redémarrages du système.

Vous pouvez vous demander si «unité» dans le test d'unité désigne une «unité de code» ou une «unité de fonctionnalité», fonctionnalité créée peut-être par plusieurs unités de code de concert. Je ne trouve pas cela une distinction utile - les questions que je garderais à l'esprit sont les suivantes: "le test indique-t-il si le système fournit-il une valeur commerciale?" Et "le test est-il fragile si la mise en œuvre change?" Des tests tels que celui décrit sont utiles lorsque vous utilisez le système (vous n’avez pas encore écrit «extraire un objet de la base de données»), vous ne pouvez donc pas tester l’ensemble des fonctionnalités - mais vous ne supportez pas les modifications d’implémentation. retirez-les une fois que l'opération complète est testable.

Pete Kirkham
la source
1

L'esprit est correct.

Idéalement, dans un test unitaire , vous testez une unité (méthode individuelle ou petite classe).

Idéalement, vous réduirez tout le système de base de données. En d'autres termes, vous exécuteriez votre méthode dans un environnement fictif et assurez-vous simplement qu'elle appelle les API de base de données correctes dans le bon ordre. Vous ne souhaitez explicitement pas tester la base de données lorsque vous testez l'une de vos propres méthodes.

Les avantages sont nombreux. Surtout, les tests deviennent vite aveuglants car vous n'avez pas besoin de vous préoccuper de la configuration d'un environnement de base de données correct et de sa restauration ultérieure.

Bien entendu, il s'agit d'un objectif ambitieux dans tout projet logiciel qui ne le fait pas dès le départ. Mais c'est l'esprit des tests unitaires .

Notez qu'il existe d'autres tests, tels que les tests de fonctionnalité, les tests de comportement, etc. qui diffèrent de cette approche - ne confondez pas "test" avec "test unitaire".

AnoE
la source
"Assurez-vous simplement qu'il appelle les API de base de données correctes dans le bon ordre" - ouais, bien sûr. En attendant, vous avez mal interprété la documentation et l'ordre exact que vous auriez dû utiliser est complètement différent. Ne vous moquez pas des systèmes en dehors de votre contrôle.
Joker_vD
4
@Joker_vD C'est à cela que servent les tests d'intégration. Dans les tests unitaires, il faut absolument se moquer des systèmes externes.
Ben Aaronson
1

Il existe au moins une raison très importante de ne pas utiliser l'accès direct à la base de données dans vos tests unitaires pour le code métier qui interagit avec la base de données: si vous modifiez l'implémentation de votre base de données, vous devrez réécrire tous ces tests unitaires. Renommez une colonne. Pour votre code, il vous suffit de modifier une seule ligne dans la définition de votre mappeur de données. Toutefois, si vous n'utilisez pas votre mappeur de données lors des tests, vous devrez également modifier chaque test unitaire faisant référence à cette colonne . Cela pourrait se transformer en une quantité de travail extraordinaire, en particulier pour les changements plus complexes qui se prêtent moins à la recherche et au remplacement.

En outre, utiliser votre mappeur de données en tant que mécanisme abstrait plutôt que de communiquer directement avec la base de données facilite la suppression complète de la dépendance à la base de données , ce qui peut ne pas être pertinent maintenant, mais lorsque vous obtenez plusieurs milliers de tests unitaires et tous. Si vous frappez une base de données, vous serez reconnaissant de pouvoir facilement les reformuler pour supprimer cette dépendance, car votre suite de tests passera de quelques minutes à exécuter à quelques secondes, ce qui peut avoir un impact considérable sur votre productivité.

Votre question indique que vous réfléchissez dans la bonne direction. Comme vous le soupçonnez, les tests unitaires sont également codés. Il est tout aussi important de les rendre faciles à gérer et à adapter aux modifications futures que pour le reste de votre base de code. Efforcez-vous de les garder lisibles et d’éliminer les doublons, et vous disposerez d’une suite de tests bien meilleure. Et une bonne suite de tests est celle qui s'habitue. Et une suite de tests utilisée permet de détecter les erreurs, tandis qu'une suite de tests non utilisée ne sert à rien.

Periata Breatta
la source
1

La meilleure leçon que j'ai apprise lors de l'apprentissage des tests unitaires et des tests d'intégration est de ne pas tester les méthodes, mais de tester les comportements. En d'autres termes, que fait cet objet?

Quand je le vois de cette façon, une méthode qui conserve les données et une autre méthode qui les lit commencent à être testables. Si vous testez les méthodes spécifiquement, bien sûr, vous vous retrouvez avec un test pour chaque méthode isolément, tel que:

@Test
public void canSaveData() {
    writeDataToDatabase();
    // what can you assert - the only expectation you can have here is that an exception was not thrown.
}

@Test
public void canReadData() {
    // how do I even get data in there to read if I cannot call the method which writes it?
}

Ce problème est dû à la perspective des méthodes de test. Ne testez pas les méthodes. Tester les comportements. Quel est le comportement de la classe WidgetDao? Il persiste les widgets. Ok, comment vérifiez-vous qu'il persiste widgets? Eh bien, quelle est la définition de la persistance? Cela signifie que lorsque vous l'écrivez, vous pouvez le relire. Ainsi, lire et écrire ensemble deviennent un test et, à mon avis, un test plus significatif.

@Test
public void widgetsCanBeStored() {
    Widget widget = new Widget();
    widget.setXXX.....
    // blah

    widgetDao.storeWidget(widget);
    Widget stored = widgetDao.getWidget(widget.getWidgetId());
    assertEquals(widget, stored);
}

C’est un test logique, cohérent, fiable et, à mon avis, significatif.

Les autres réponses montrent à quel point l'isolement est important, le débat pragmatique versus réaliste, et si un test unitaire peut interroger ou non une base de données. Ceux-ci ne répondent pas vraiment à la question cependant.

Pour tester si quelque chose peut être stocké, vous devez le stocker puis le relire. Vous ne pouvez pas tester si quelque chose est stocké si vous n'êtes pas autorisé à le lire. Ne testez pas le stockage des données séparément de la récupération des données. Vous allez vous retrouver avec des tests qui ne vous disent rien.

Brandon
la source
Approche très perspicace, et comme vous le dites, je pense que vous avez vraiment mis l’un de mes doutes au premier plan. Merci!
carlossierra
0

Un exemple d'échec que vous préféreriez éviter, est que l'objet testé utilise une couche de mise en cache mais ne conserve pas les données comme requis. Ensuite, si vous interrogez l'objet, le message "Ouais, j'ai le nouveau nom et l'adresse", mais vous voulez que le test échoue car il n'a pas réellement fait ce qu'il était censé faire.

Sinon (et en négligeant la violation de responsabilité unique), supposons qu'il soit nécessaire de conserver une version codée en UTF-8 de la chaîne dans un champ orienté octet, mais en fait, elle persiste avec Shift JIS. Un autre composant va lire la base de données et s'attend à voir UTF-8, d'où l'exigence. Ensuite, l'aller-retour à travers cet objet indiquera le nom et l'adresse corrects car il le reconvertira à partir de Shift JIS, mais l'erreur n'est pas détectée par votre test. Espérons que cela sera détecté par un test d'intégration ultérieur, mais l'objectif des tests unitaires est de détecter les problèmes plus tôt et de savoir exactement quel composant est responsable.

Si l'un d'entre eux ne fait pas ce qu'il est censé faire, son propre scénario de test échouera et nous pourrons le réparer et exécuter à nouveau la batterie de test.

Vous ne pouvez pas supposer cela, car si vous ne faites pas attention, vous écrivez un ensemble de tests mutuellement dépendants. Le "est-ce que ça sauve?" test appelle la méthode de sauvegarde testée, puis la méthode de chargement pour confirmer la sauvegarde. Le "charge-t-il?" test appelle la méthode save pour configurer le dispositif de test, puis la méthode de chargement testée pour vérifier le résultat. Les deux tests reposent sur l'exactitude de la méthode qu'ils ne testent pas, ce qui signifie qu'aucun des deux ne teste réellement l'exactitude de la méthode testée.

L'indice qu'il y a un problème ici est que deux tests supposés tester différentes unités font la même chose . Ils appellent tous deux un setter suivi d'un getter, puis vérifient que le résultat correspond à la valeur d'origine. Mais vous vouliez vérifier que le setter persiste dans les données, mais que la paire setter / getter fonctionne ensemble. Donc, vous savez que quelque chose ne va pas, il vous suffit de trouver quoi et de corriger les tests.

Si votre code est bien conçu pour les tests unitaires, vous avez au moins deux méthodes pour vérifier si les données ont bien été conservées correctement par la méthode testée:

  • simulez l'interface de base de données et demandez à votre simulacre d'enregistrer le fait que les fonctions appropriées ont été appelées, avec les valeurs attendues. Cela teste la méthode et fait le test unitaire classique.

  • transmettez-lui une base de données réelle avec exactement la même intention , pour enregistrer si les données ont été correctement conservées ou non. Mais plutôt que d'avoir une fonction fictive qui se contente de dire "ouais, j'ai les bonnes données", votre test lit directement dans la base de données et confirme que c'est correct. Ce n'est peut-être pas le test le plus pur possible, car tout un moteur de base de données est une très grosse chose à utiliser pour écrire une maquette glorifiée, avec plus de chance que j'oublie une certaine subtilité qui fait passer un test même si quelque chose ne va pas (par exemple, je ne devriez pas utiliser la même connexion de base de données pour lire que celle utilisée pour écrire, car je pourrais voir une transaction non validée). Mais il teste la bonne chose, et au moins vous savez que c'est précisément implémente toute l'interface de la base de données sans avoir à écrire de code factice!

Il s’agit donc d’un simple détail d’implémentation de test, que je lise les données de la base de données de test par JDBC ou que je moque la base de données. Quoi qu'il en soit, le fait est que je peux mieux tester l'unité en l'isolant que si je lui permettais de conspirer avec d'autres méthodes incorrectes de la même classe pour obtenir une apparence correcte, même en cas de problème. Par conséquent, je souhaite utiliser tout moyen pratique pour vérifier que les données correctes ont bien été conservées, en dehors de la confiance envers le composant dont je teste la méthode.

Si votre code n'est pas bien conçu pour les tests unitaires, vous n'avez peut-être pas le choix, car l'objet dont vous souhaitez tester la méthode risque de ne pas accepter la base de données en tant que dépendance injectée. Dans ce cas, la discussion sur le meilleur moyen d'isoler l'unité à tester passe à une discussion sur le degré de proximité possible pour isoler l'unité à tester. La conclusion est la même, cependant. Si vous pouvez éviter les complots entre unités défectueuses, alors vous le ferez, sous réserve du temps disponible et de tout ce que vous pensez qui serait plus efficace pour trouver des erreurs dans le code.

Steve Jessop
la source
0

C'est comme ça que j'aime le faire. Ceci est juste un choix personnel puisque cela n’a pas d’impact sur le résultat du produit, mais uniquement sur la manière de le produire.

Cela dépend des possibilités pour pouvoir ajouter des valeurs fictives dans votre base de données sans l'application que vous testez.

Supposons que vous avez deux tests: A - lecture de la base de données B - insertion dans la base de données (dépend de A)

Si A réussit, vous êtes sûr que le résultat de B dépend de la partie testée dans B et non des dépendances. Si A échoue, vous pouvez avoir un faux négatif pour B. L'erreur peut être en B ou en A. Vous ne pouvez pas savoir avec certitude jusqu'à ce que A revienne avec succès.

Je n'essaierais pas d'écrire des requêtes dans un test unitaire car vous ne devriez pas pouvoir connaître la structure de la base de données derrière l'application (sinon elle pourrait changer). Cela signifie que votre scénario de test pourrait échouer à cause de votre code lui-même. Sauf si vous écrivez un test pour le test, mais qui le testera pour le test ...

AxelH
la source
-1

En fin de compte, cela dépend de ce que vous voulez tester.

Parfois, il suffit de tester l'interface fournie par votre classe (par exemple, l'enregistrement est créé et stocké et peut être récupéré plus tard, éventuellement après un "redémarrage"). Dans ce cas, il est bon (et généralement une bonne idée) d’utiliser les méthodes fournies pour les tests. Cela permet de réutiliser les tests, par exemple si vous souhaitez modifier le mécanisme de stockage (base de données différente, base de données en mémoire pour les tests, ...).

Notez que cela signifie que vous voulez uniquement que la classe adhère à son contrat (créer, stocker et récupérer des enregistrements dans ce cas), et non pas comment elle y parvient. Si vous créez intelligemment vos tests unitaires (par rapport à une interface, pas directement à une classe), vous pouvez tester différentes implémentations, car elles doivent également respecter le contrat de l'interface.

D'autre part, dans certains cas, vous devez réellement vérifier comment les éléments sont persistants (un autre processus dépend peut-être du fait que les données sont au bon format dans cette base de données?). Maintenant, vous ne pouvez plus vous contenter de récupérer le bon enregistrement dans la classe. Vous devez plutôt vérifier qu'il est correctement stocké dans la base de données. Et le moyen le plus direct de procéder consiste à interroger la base de données elle-même.

Maintenant, vous vous souciez non seulement de la classe qui respecte son contrat (créer, stocker et récupérer), mais aussi de la manière dont elle l'obtient (car les données persistantes doivent adhérer à une autre interface). Cependant, à présent, les tests dépendent à la fois de l’interface de classe et du format de stockage. Il sera donc difficile de les réutiliser pleinement. (Certains pourraient appeler ces types de tests des "tests d'intégration".)

hoffmale
la source
@ downvoters: pourriez-vous me dire vos raisons pour que je puisse améliorer les aspects qui, selon vous, manquent à ma réponse?
hoffmale