Il y a quelque temps, j'ai lu, sur une réponse Stack Overflow que je ne trouve pas, une phrase qui expliquait que vous devriez tester les API publiques, et l'auteur a dit que vous devriez tester les interfaces. L'auteur a également expliqué que si une implémentation de méthode changeait, vous ne devriez pas avoir besoin de modifier le scénario de test, car cela romprait le contrat garantissant le fonctionnement du système testé. En d'autres termes, un test doit échouer si la méthode ne fonctionne pas, mais pas parce que l'implémentation a changé.
Cela a attiré mon attention lorsque nous parlons de moqueries. Étant donné que le mocking repose fortement sur les appels d'attente du système sous les dépendances du test, les mocks sont étroitement associés à l'implémentation plutôt qu'à l'interface.
Lors de la recherche de simulateurs contre stub , plusieurs articles conviennent que les stubs doivent être utilisés à la place des simulacres, car ils ne reposent pas sur les attentes des dépendances, ce qui signifie que le test n'a pas besoin de connaître le système sous-jacent sous la mise en œuvre du test.
Mes questions seraient:
- Les moqueries violent-elles le principe ouvert / fermé?
- Y a-t-il quelque chose qui manque dans l'argument en faveur des talons du dernier paragraphe, qui fait que les talons ne sont pas si grands contre les moqueries?
- Si oui, quel serait un bon cas d'utilisation pour se moquer et quand serait un bon cas d'utilisation pour utiliser des talons?
la source
Since mocking relays heavily on expectation calls from system under test's dependencies...
Je pense que c'est là que vous allez de travers. Une maquette est une représentation artificielle d'un système externe. Il ne représente en aucun cas le système externe, sauf dans la mesure où il simule le système externe de manière à permettre l'exécution de tests sur du code ayant des dépendances sur ledit système externe. Vous aurez toujours besoin de tests d'intégration pour prouver que votre code fonctionne avec le vrai système non simulé.Réponses:
Je ne vois pas pourquoi les moqueries violeraient le principe ouvert / fermé. Si vous pouviez nous expliquer pourquoi vous pensez qu'ils le pourraient, alors nous pourrons peut-être atténuer vos préoccupations.
Le seul inconvénient des stubs auquel je peux penser est qu'ils nécessitent généralement plus de travail pour écrire que les mocks, car chacun d'eux est en fait une implémentation alternative d'une interface dépendante, il doit donc généralement fournir une version complète (ou convaincante) implémentation de l'interface dépendante. Pour vous donner un exemple extrême, si votre sous-système sous test invoque un SGBDR, alors une maquette du SGBDR répondrait simplement à des requêtes spécifiques connues pour être émises par le sous-système sous test, produisant des ensembles prédéterminés de données de test. D'un autre côté, une implémentation alternative serait un SGBDR complet en mémoire, éventuellement avec le fardeau supplémentaire d'avoir à émuler les caprices du SGBDR client-serveur réel que vous utilisez en production. (Heureusement, nous avons des choses comme HSQLDB, donc nous pouvons en fait faire ça, mais quand même,
Les bons cas d'utilisation pour la simulation sont lorsque l'interface dépendante est trop compliquée pour écrire une implémentation alternative pour elle, ou si vous êtes sûr que vous n'écrirez la simulation qu'une seule fois et ne la toucherez plus jamais. Dans ces cas, allez-y et utilisez une maquette rapide et sale. Par conséquent, les bons cas d'utilisation des stubs (implémentations alternatives) sont à peu près tout le reste. Surtout si vous prévoyez de vous engager dans une relation à long terme avec le sous-système testé, optez définitivement pour une implémentation alternative qui sera agréable et propre, et nécessitera une maintenance uniquement en cas de changement d'interface, au lieu d'exiger une maintenance chaque fois que l'interface changements et chaque fois que la mise en œuvre du sous-système sous test change.
PS La personne à laquelle vous faites référence pourrait être moi, dans l'une de mes autres réponses liées aux tests ici sur programmers.stackexchange.com, par exemple celle-ci .
la source
an alternative implementation would be a full-blown in-memory RDBMS
- Vous ne devez pas nécessairement aller aussi loin avec un talon.Le principe Open / Closed consiste principalement à pouvoir changer le comportement d'une classe sans le modifier. Par conséquent, l'injection d'une dépendance de composant simulée dans une classe en cours de test ne la viole pas.
Le problème avec les doubles de test (mock / stub) est que vous faites essentiellement des hypothèses arbitraires concernant la façon dont la classe testée interagit avec son environnement. Si ces attentes sont fausses, eh bien, vous risquez d'avoir des problèmes une fois le code déployé. Si vous pouvez vous le permettre, testez votre code dans les mêmes contraintes que celle qui limite votre environnement de production. Si vous ne le pouvez pas, faites le moins d'hypothèses possibles, et simulez / stubez uniquement les périphériques de votre système (base de données, service d'authentification, client HTTP, etc ...).
La seule raison valable pour laquelle, à mon humble avis, un double doit être utilisé, c'est lorsque vous devez enregistrer ses interactions avec la classe en cours de test, ou lorsque vous devez fournir de fausses données (ce que les deux techniques peuvent faire). Attention cependant, en abuser reflète une mauvaise conception, ou un test qui repose trop sur l'API en cours d'implémentation.
la source
Remarque: je suppose que vous définissez Mock pour signifier "une classe sans implémentation, juste quelque chose que vous pouvez surveiller" et Stub pour être "partiellement mock, aka utilise certains des comportements réels de la classe implémentée", selon cette pile Question de débordement .
Je ne sais pas pourquoi vous pensez que le consensus est d'utiliser des talons, par exemple c'est tout le contraire dans la documentation Mockito
Cette documentation le dit mieux que moi. L'utilisation de simulateurs vous permet de simplement tester cette classe particulière, et rien d'autre; si vous avez besoin de moqueries partielles pour obtenir le comportement que vous recherchez, vous avez probablement fait quelque chose de mal, violez SRP, etc., et votre code pourrait tenir lieu de refactor. Les maquettes ne violent pas le principe ouvert-fermé, car elles ne sont utilisées que dans les tests de toute façon, ce ne sont pas de véritables modifications de ce code. Habituellement, ils sont générés à la volée de toute façon par une bibliothèque comme cglib.
la source
Je pense que le problème peut découler de l'hypothèse que les seuls tests valides sont ceux qui satisfont au test ouvert / fermé.
Il est facile de voir que le seul test qui importe est celui qui teste l'interface. Cependant, en réalité, il est souvent plus efficace de tester cette interface en testant le fonctionnement interne.
Par exemple, il est presque impossible de tester une exigence négative, telle que «la mise en œuvre ne lèvera aucune exception». Considérez une interface de carte implémentée avec une table de hachage. Vous voulez être certain que le hashmap rencontre l'interface de la carte, sans lancer, même quand il doit refaire des choses (ce qui pourrait devenir risqué). Vous pouvez tester chaque combinaison d'entrées pour vous assurer qu'elles répondent aux exigences de l'interface, mais cela peut prendre plus de temps que la mort thermique de l'univers. Au lieu de cela, vous cassez un peu l'encapsulation et développez des simulations qui interagissent plus étroitement, forçant la table de hachage à faire exactement la refonte nécessaire pour garantir que l'algorithme de rehachage ne lance pas.
Tl / Dr: le faire "par le livre" est bien, mais quand le coup de pouce arrive, avoir un produit sur le bureau de votre patron d'ici vendredi est plus utile qu'une suite de tests par le livre qui prend jusqu'à la mort de la chaleur du univers pour confirmer la conformité.
la source