Question

Lorsque j’ai découvert Mocks, j’ai senti que le but principal était de simuler des objets provenant de sources de données externes.De cette façon, je n'avais pas besoin de maintenir une base de données de tests unitaires automatisés, je pouvais simplement la simuler.

Mais maintenant, je commence à y penser différemment.Je me demande si les simulations sont plus efficaces pour isoler complètement la méthode testée de tout ce qui est extérieur à elle-même.L’image qui vient sans cesse à l’esprit est la toile de fond que vous utilisez lorsque vous peignez.Vous voulez empêcher la peinture de se répandre partout.Je teste seulement cette méthode, et je veux seulement savoir comment elle réagit à ces faux facteurs externes ?

Cela semble incroyablement fastidieux de procéder de cette façon, mais l'avantage que je vois est que lorsque le test échoue, c'est parce qu'il est foutu et non pas 16 couches plus bas.Mais maintenant, je dois effectuer 16 tests pour obtenir la même couverture de test, car chaque pièce serait testée isolément.De plus, chaque test devient plus compliqué et plus profondément lié à la méthode qu’il teste.

Cela me semble bien, mais cela semble aussi brutal, donc je veux en quelque sorte savoir ce que pensent les autres.

Était-ce utile?

La solution

Je vous recommande de jeter un oeil à l'article de Martin Fowler Les moqueries ne sont pas des talons pour un traitement des Mocks plus autoritaire que ce que je peux vous donner.

Le but des simulations est de tester unitairement votre code indépendamment des dépendances afin que vous puissiez véritablement tester un morceau de code au niveau « unité ».Le code testé est la vraie affaire, et tous les autres morceaux de code sur lesquels il s'appuie (via des paramètres ou une injection de dépendances, etc.) sont un "Mock" (une implémentation vide qui renvoie toujours les valeurs attendues lorsqu'une de ses méthodes est appelée.)

Les simulations peuvent sembler fastidieuses au début, mais elles rendent les tests unitaires beaucoup plus faciles et plus robustes une fois que vous maîtrisez leur utilisation.La plupart des langages ont des bibliothèques Mock qui rendent la moquerie relativement triviale.Si vous utilisez Java, je vous recommande mon favori personnel : FacileMock.

Permettez-moi de terminer avec cette pensée :vous avez également besoin de tests d'intégration, mais disposer d'un bon volume de tests unitaires vous aide à découvrir quel composant contient un bug, lorsqu'il existe.

Autres conseils

Ne suivez pas le chemin sombre, Maître Luke.:) Ne vous moquez pas de tout.Vous pourriez mais vous ne devriez pas...Voici pourquoi.

  • Si vous continuez à tester chaque méthode isolément, vous aurez des surprises et du pain sur la planche lorsque vous les rassemblerez toutes ensemble. BIG BANG.Nous construisons des objets afin qu’ils puissent travailler ensemble pour résoudre un problème plus important.En eux-mêmes, ils sont insignifiants.Toi besoin de savoir si tous les collaborateurs travaillent comme prévu.
  • Se moque rendre les tests fragiles en introduisant la duplication - Oui, je sais, cela semble alarmant.Pour chaque simulation que vous configurez, il existe n endroits où votre signature de méthode existe.Le code réel et vos attentes fictives (dans plusieurs tests).Changer le code réel est plus facile...mettre à jour toutes les attentes fictives est fastidieux.
  • Ton le test est désormais au courant des informations d'implémentation privilégiées.Votre test dépend donc de la manière dont vous avez choisi d'implémenter la solution...mauvais.Les tests doivent être une spécification indépendante pouvant être satisfaite par plusieurs solutions.Je devrais avoir la liberté d'appuyer simplement sur Supprimer un bloc de code et de le réimplémenter sans devoir réécrire la suite de tests ..parce que les exigences restent toujours les mêmes.

Pour terminer, je dirai "S'il cancane comme un canard, marche comme un canard, alors c'est probablement un canard" - Si ça ne va pas..c'est probablement le cas.*Utilisez des simulations pour extraire les enfants problématiques tels que les opérations d'E/S, les bases de données, les composants tiers, etc.Comme le sel, une partie est nécessaire.trop et :x *
Il s’agit de la guerre sainte entre les tests basés sur l’État et les tests basés sur l’itération.La recherche sur Google vous donnera un aperçu plus approfondi.

Clarification:Je rencontre une certaine résistance par rapport àtests d'intégration ici :) Alors pour clarifier ma position..

  • Les simulations ne figurent pas dans le domaine des « tests d'acceptation »/d'intégration.Vous ne les trouverez que dans le monde des tests unitaires.et c'est mon objectif ici.
  • Les tests d'acceptation sont différents et sont beaucoup nécessaires - sans les dévaloriser.Mais les tests unitaires et les tests d'acceptation sont différents et doivent rester différents.
  • Tous les collaborateurs d'un composant ou d'un package n'ont pas besoin d'être isolés les uns des autres.Comme la micro-optimisation, c'est Overkill.Ils existent pour résoudre un problème ensemble..cohésion.

Oui je suis d'accord.Je considère que se moquer est parfois douloureux, mais souvent nécessaire, pour que vos tests deviennent réellement unité des tests, c'est-à-direseule la plus petite unité sur laquelle vous pouvez effectuer votre test est en cours de test.Cela vous permet d’éliminer tout autre facteur susceptible d’affecter le résultat du test.Vous vous retrouvez avec beaucoup plus de petits tests, mais il devient beaucoup plus facile de déterminer où se situe le problème avec votre code.

Ma philosophie est que vous devez écrire du code testable pour s'adapter aux tests,
ne pas écrire de tests pour s'adapter au code.

En ce qui concerne la complexité, mon avis est que les tests devraient être simples à écrire, simplement parce que vous écrivez plus de tests s'ils le sont.

Je pourrais être d'accord que cela pourrait être une bonne idée si les classes dont vous vous moquez n'ont pas de suite de tests, car si elles disposaient d'une suite de tests appropriée, vous saurez où se situe le problème sans isolement.

La plupart du temps que j'ai utilisé pour des objets fictifs, c'est lorsque le code pour lequel j'écris des tests est si étroitement couplé (lire :mauvaise conception), que je dois écrire des objets fictifs lorsque les classes dont ils dépendent ne sont pas disponibles.Bien sûr, il existe des utilisations valides pour les objets fictifs, mais si votre code a besoin leur utilisation, je jetterais un autre regard sur le design.

Oui, c'est l'inconvénient des tests avec des simulations.Il y a beaucoup de travail à faire et cela semble brutal.Mais c’est l’essence des tests unitaires.Comment tester quelque chose de manière isolée si vous ne vous moquez pas des ressources externes ?

D'un autre côté, vous vous moquez des fonctionnalités lentes (telles que les bases de données et les opérations d'E/S).Si les tests s’exécutent plus rapidement, les programmeurs seront satisfaits.Il n'y a rien de plus pénible que d'attendre des tests très lents, qui prennent plus de 10 secondes pour s'exécuter, pendant que vous essayez d'implémenter une fonctionnalité.

Si chaque développeur de votre projet passait du temps à écrire des tests unitaires, alors ces 16 couches (d'indirection) ne poseraient pas vraiment de problème.J'espère que vous devriez bénéficier de cette couverture de test dès le début, n'est-ce pas ?:)

N'oubliez pas non plus d'écrire un test de fonction/intégration entre objets en collaboration.Ou bien vous pourriez manquer quelque chose.Ces tests n’auront pas besoin d’être exécutés souvent, mais ils restent importants.

À une certaine échelle, oui, les simulations sont destinées à être utilisées pour simuler des sources de données externes telles qu'une base de données ou un service Web.Cependant, à une échelle plus fine, si vous concevez du code faiblement couplé, vous pouvez tracer des lignes presque arbitrairement dans votre code quant à ce qui pourrait à tout moment être un « système extérieur ».Prenez un projet sur lequel je travaille actuellement :

Lorsque quelqu'un tente de s'enregistrer, le CheckInUi envoie un Informations d'enregistrement s'opposer à un CheckInMediator objet qui le valide à l'aide d'un CheckInValidator, alors si tout va bien, il remplit un objet de domaine nommé Transaction avec Informations d'enregistrement en utilisant CheckInInfoAdaptateur passe ensuite le Transaction à une instance de ITransactionDao.SaveTransaction() pour la persévérance.

J'écris en ce moment des messages automatisés tests d'intégration et évidemment le CheckInUi et ITransactionDao sont des fenêtres sur des systèmes externes et ce sont eux dont il faut se moquer.Cependant, qui peut dire cela à un moment donné CheckInValidator vous n'appellerez pas un service Web ?C'est pourquoi quand tu écris tests unitaires vous supposez que tout autre que la fonctionnalité spécifique de votre classe est un système externe.Par conséquent, dans mon test unitaire de CheckInMediator Je me moque de tous les objets avec lesquels il parle.

MODIFIER: Gishu est techniquement correct, il n'est pas nécessaire de se moquer de tout, je ne me moque pas par exemple Informations d'enregistrement puisqu'il s'agit simplement d'un conteneur de données.Cependant, tout ce que vous pourriez considérer comme un service externe (et c'est presque tout ce qui transforme des données ou a des effets secondaires) doit être ridiculisé.

Une analogie que j’aime est de considérer une conception correctement couplée comme un champ avec des gens autour de lui jouant à un jeu de capture.Lorsque quelqu'un reçoit le ballon, il peut lancer une balle complètement différente à la personne suivante, il peut même lancer plusieurs balles successivement à différentes personnes ou lancer une balle et attendre de la recevoir avant de la lancer à une autre personne.C'est un jeu étrange.

Désormais, en tant qu'entraîneur et manager, vous souhaitez bien sûr vérifier le fonctionnement de votre équipe dans son ensemble afin de vous entraîner en équipe (tests d'intégration), mais vous souhaitez également que chaque joueur s'entraîne seul contre des backstops et des machines à lancer la balle (tests unitaires avec se moque).La seule pièce qui manque sur cette image, ce sont des attentes simulées et nous avons donc nos balles enduites de goudron noir afin qu'elles tachent le filet de sécurité lorsqu'elles le frappent.Chaque filet de sécurité a une « zone cible » que la personne vise et si à la fin d'un entraînement, il n'y a pas de marque noire dans la zone cible, vous savez que quelque chose ne va pas et que la personne a besoin d'ajuster sa technique.

Il faut vraiment prendre le temps de bien l'apprendre, le jour où j'ai compris Mocks a été un énorme moment a-ha.Combinez-le avec une inversion de conteneur de contrôle et je n'y retournerai jamais.

En passant, un de nos informaticiens vient d'arriver et m'a offert un ordinateur portable gratuit !

Comme quelqu'un l'a déjà dit, si vous vous moquez de tout pour isoler de manière plus granulaire que la classe que vous testez, vous abandonnez l'application de la cohésion dans votre code en cours de test.

Gardez à l’esprit que la moquerie présente un avantage fondamental : la vérification du comportement.C'est quelque chose que les stubs ne fournissent pas et c'est l'autre raison qui rend le test plus fragile (mais peut améliorer la couverture du code).

Les simulations ont été inventées en partie pour répondre à la question:Comment testeriez-vous des objets s'ils n'avaient ni getters ni setters ?

Ces jours, recommandé la pratique consiste à se moquer des rôles et non des objets.Utilisez les Mocks comme un outil de conception pour parler de collaboration et de séparation des responsabilités, et non comme des « stubs intelligents ».

Les objets simulés sont 1) souvent utilisés comme moyen d'isoler le code testé, MAIS 2) comme Keithb l'a déjà souligné, sont importants pour "se concentrer sur les relations entre objets collaborants".Cet article donne quelques aperçus et historiques liés au sujet : Conception axée sur la responsabilité avec des objets simulés.

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