J’ai récemment discuté avec un collègue de la moquerie. Il a dit que se moquer des cours est très mauvais et ne devrait pas être fait, seulement dans quelques cas.
Il dit que seules les interfaces devraient être moquées, sinon c’est une faute d’architecture.
Je me demande pourquoi cette déclaration (je lui fais entièrement confiance) est si correcte? Je ne le sais pas et je voudrais être convaincu.
Ai-je raté le sharepoint me moquer (oui, j’ai lu l’article de Martin Fowler )
Mocking est utilisé pour les tests de protocole – il teste comment vous allez utiliser une API et comment vous allez réagir lorsque l’API réagit en conséquence.
Idéalement (dans bien des cas au moins), cette API devrait être spécifiée comme une interface plutôt qu’une classe – une interface définit un protocole, une classe définit au moins une partie d’une implémentation.
Sur un plan pratique, les frameworks moqueurs ont tendance à avoir des limites autour des classes moqueuses.
Dans mon expérience, moqueur est un peu surutilisé – souvent vous n’êtes pas vraiment intéressé par l’interaction exacte, vous voulez vraiment un stub … mais un cadre moqueur peut être utilisé pour créer des stubs, et vous tombez dans le piège de créer des tests fragiles en se moquer au lieu de taper. C’est un équilibre difficile à faire si bien.
IMHO, ce que votre collègue signifie, c’est que vous devez programmer pour une interface, pas une implémentation . Si vous vous moquez trop souvent des cours, c’est un signe que vous avez enfreint le principe précédent lors de la conception de votre architecture.
Les classes moqueuses (contrairement aux interfaces moqueuses) sont mauvaises car la maquette a toujours une classe réelle en arrière-plan, elle en hérite et il est possible qu’une implémentation réelle soit exécutée pendant le test .
Lorsque vous vous moquez d’une interface (ou d’un stub ou autre), il n’y a aucun risque que le code soit exécuté et que vous vouliez réellement vous moquer.
Les classes moqueuses vous obligent également à faire en sorte que tout, qui pourrait être raillé, soit virtuel , ce qui est très intrusif et pourrait mener à une mauvaise conception de classe .
Si vous voulez découpler les classes, elles ne doivent pas se connaître, c’est la raison pour laquelle il est logique de se moquer d’elles (ou de les remplacer). La mise en œuvre contre les interfaces est donc recommandée, mais cela est déjà mentionné ici par d’autres.
En général, vous souhaitez simuler une interface.
Bien qu’il soit possible de se moquer d’une classe régulière, cela a tendance à trop influencer la conception de votre classe pour la testabilité. Des préoccupations telles que l’ accessibilité, le fait qu’une méthode soit virtuelle ou non, seront toutes déterminées par la capacité à se moquer de la classe plutôt que par de véritables préoccupations d’OO.
Il existe une fausse bibliothèque appelée TypeMock Isolator qui vous permet de contourner ces limitations (prenez un gâteau, mangez un gâteau), mais c’est assez cher. Mieux vaut concevoir pour la testabilité.
Je suggère de restr loin des frameworks moqueurs dans la mesure du possible. Dans le même temps, je recommande d’utiliser des objects fictifs pour tester, autant que possible. L’astuce est que vous devez créer de faux objects intégrés avec des objects réels. Je l’explique plus en détail dans un article de blog que j’ai écrit à ce sujet: http://www.yegor256.com/2014/09/23/built-in-fake-objects.html
Je parle de simulacre et de stub tel que défini par Martin Fowler , et je suppose que c’est ce que votre collègue voulait dire également.
Se moquer est mauvais car cela peut conduire à une sur-spécification des tests. Utilisez le talon si possible et évitez de vous moquer.
Voici le diff entre mock et stub (de l’article ci-dessus):
Nous pouvons alors utiliser la vérification d’état sur le talon comme ceci.
class OrderStateTester... public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); MailServiceStub mailer = new MailServiceStub(); order.setMailer(mailer); order.fill(warehouse); assertEquals(1, mailer.numberSent()); }
Bien sûr, il s’agit d’un test très simple: un message a été envoyé. Nous ne l’avons pas testé, il a été envoyé à la bonne personne, ou avec le bon contenu, mais cela le fera pour illustrer ce point.
En utilisant des simulacres, ce test serait très différent.
class OrderInteractionTester... public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); Mock warehouse = mock(Warehouse.class); Mock mailer = mock(MailService.class); order.setMailer((MailService) mailer.proxy()); mailer.expects(once()).method("send"); warehouse.expects(once()).method("hasInventory") .withAnyArguments() .will(returnValue(false)); order.fill((Warehouse) warehouse.proxy()); } }
Afin d’utiliser la vérification d’état sur le stub, je dois utiliser des méthodes supplémentaires sur le stub pour faciliter la vérification. En conséquence, le module de remplacement implémente MailService mais ajoute des méthodes de test supplémentaires.
La réponse, comme la plupart des questions sur les pratiques, est “ça dépend”.
La surutilisation des simulacres peut mener à des tests qui ne testent pas vraiment quoi que ce soit. Il peut également conduire à des tests qui sont des ré-implémentations virtuelles du code sous test, étroitement liés à une implémentation spécifique.
D’un autre côté, une utilisation judicieuse des simulacres et des stubs peut conduire à des tests unitaires soigneusement isolés et à tester une chose et une seule, ce qui est une bonne chose.
Tout est question de modération.
Il est logique de simuler des cours pour que les tests puissent être écrits au début du cycle de développement.
Il y a une tendance à continuer à utiliser des classes simulées même lorsque des implémentations concrètes deviennent disponibles. Il y a également une tendance à se développer contre les classes simulées (et les stubs) nécessaires au début d’un projet lorsque certaines parties du système n’ont pas été construites.
Une fois qu’un élément du système a été construit, il est nécessaire de le tester et de continuer à le tester (pour la régression). Dans ce cas, il est bon de commencer avec des simulacres, mais ils doivent être jetés au profit de la mise en œuvre dès que possible. J’ai vu des projets en difficulté parce que différentes équipes continuent de se développer face au comportement du simulacre plutôt qu’à la mise en œuvre (une fois disponible).
En testant contre les moqueries, vous supposez que le simulacre est caractéristique du système. Cela implique souvent de deviner ce que fera le composant simulé. Si vous avez une spécification du système dont vous vous moquez, vous n’avez pas à deviner, mais souvent le système «tel que construit» ne correspond pas aux spécifications d’origine en raison de considérations pratiques découvertes pendant la construction. Les projets de développement agile supposent que cela se produira toujours.
Vous développez ensuite un code qui fonctionne avec le simulacre. Quand il s’avère que le simulacre ne représente pas vraiment le comportement du système réel tel que construit (par exemple, les problèmes de latence qui ne sont pas visibles dans la simulation, les problèmes de ressources et d’efficacité non visibles, les problèmes de performances, etc.) avoir un tas de tests de moquage sans valeur que vous devez maintenant maintenir.
Je considère que l’utilisation de simulacres est précieuse au début du développement, mais ces simulacres ne devraient pas consortingbuer à la couverture du projet. Il est préférable ultérieurement de supprimer les simulations et de créer des tests d’intégration appropriés pour les remplacer. Dans le cas contraire, votre système ne sera pas testé pour la variété de conditions non simulées (ou simulées par rapport au système réel).
Donc, la question est de savoir si ou non d’utiliser des simulacres, il s’agit de savoir quand les utiliser et quand les supprimer.