Étant donné une implémentation d'usine abstraite:
public class FooFactory : IFooFactory { public IFoo Create(object param1, object param2) { return new Foo(param1, param2); } }
5 Réponses :
Vous pouvez rendre FOOFACTORY une classe générique qui s'attend à FOO en tant que paramètre et spécifiez-le pour être une simule. Calling Factory.create (...) crée ensuite une instance de la mock, et vous pourriez alors confirmer que le motif a reçu les arguments que vous attendez. P>
Cher Gödel Non. Cela teste un détail de mise en œuvre.
@Jason: pire ... Cela soulève la question de faire une usine pour cette usine pour simplifier la génération de l'objet, mais si JONTYMC demande ...
Cela dépend de ce que FOO fait avec les 2 objets d'entrée. Vérifiez que quelque chose qui foo fait avec eux qui peut facilement être interrogé. Par exemple, si les méthodes (y compris les getters ou les setters) sont appelées sur les objets, fournissent des simulacres ou des talons que vous pouvez vérifier si les choses attendues ont été appelées. S'il y a quelque chose sur l'IFOO qui peut être testé, vous pouvez transmettre des valeurs connues dans vos objets simulés pour tester après la création. Les objets eux-mêmes ne doivent pas nécessairement être accessibles au public pour vérifier que vous les avez réussi. P>
Je pourrais également vérifier que le type attendu (FOO) est renvoyé, en fonction de si les autres comportements testés dépendent de la différence entre les implémentations IFOO. P>
S'il y a des conditions d'erreur pouvant en résulter, j'essaierais de forcer les personnes aussi, que se passe-t-il si vous passez à NULL pour des objets ou des objets, etc. FOO serait testé séparément, bien sûr, afin que vous puissiez Supposons lors d'un test FOOFACTORY que FOO fait ce qu'il devrait. P>
Vous testeriez le type de retour? Qu'est-ce que sur Terre est-ce qui vous achète? Vous avez mis en place toutes ces abstractions et vous allez maintenant écrire un test qui se casse dès que vous modifiez un détail de mise en œuvre. Yikes.
@Jason Si je vais écrire un test pour plusieurs usines qui créent une implémentation IFOO, je vérifierais que chacun retourne le type de béton que j'attends. Je questionne la nécessité de tester une usine qui fait si peu d'appel à appeler nouveau, mais dans le domaine de ce qui a été demandé ...
Non. Ce n'est pas que les consommateurs de comportement de l'usine ne peuvent dépendre de (le type de retour est si code>) afin de ne pas être testé.
@Jason Les "vrais" consommateurs ne doivent absolument pas dépendre du type de retour, vous avez raison. Mais, les tests peuvent en fonction de ce que vous devez faire pour vérifier le comportement correct. Je vais modifier pour dire "pourrait", car il est vrai que ce n'est pas toujours quelque chose à rechercher.
Les tests ne devraient pas en dépendre non plus! Ils sont aussi réels que tout autre consommateur.
@Thatatchkguy "Dans le domaine de ce qui a été posé ..." J'ai offert la possibilité de ne pas les tester unitaires et laissez-la aux tests d'intégration. Je soupçonne que c'est ce que la plupart des gens font, mais je me demandais si j'avais manqué quelque chose.
Non, ceci est définitivement pour les tests d'unités. Différentes implémentations de l'interface d'usine peuvent avoir un comportement attendu différent. Ceci est clairement pour le domaine des tests unitaires.
@JONTYMC J'ai répondu à votre question en supposant que c'était un exemple hypothétique. Dans ce cas, j'ai essayé de répondre aussi largement que possible pour donner quelques idées pour votre (ou quiconque lisant cela plus tard) situation exacte. Si toute votre usine est vraiment appelée nouvelle, alors non, je ne voudrais pas tester cela. Si c'est plus impliqué, cela peut justifier des tests d'unités.
@Jason pour une usine, une partie de leur comportement attendu renvoie le type que vous attendez. Si une usine peut renvoyer une variété d'implémentations IFOO en fonction des entrées, etc., cela fait partie du comportement et je voudrais tester qu'il renvoie le "correct".
De nouveau. Le type de retour est ifoo code>, donc personne ne peut dépendre de l'un type de béton étant renvoyé. Pour ce faire, il est de s'appuyer sur un détail de mise en œuvre qui peut avoir une chance sans ce qui serait considéré comme des changements de rupture (ceux qui ne dépendent pas des détails de la mise en œuvre). Heck, l'usine pourrait renvoyer une implémentation de
ifoo code> sur un appel et une autre implémentation sur le suivant. Si vous avez besoin du type de béton, appelez simplement
nouveau foo code>; Pourquoi déranger avec l'usine? Ou, au moins, ne tapez pas le type de retour comme
ifoo code>, tapez-le comme
foo code>.
@Jason, plus je pense à cela, dans la plupart des situations, je conviens que même les tests ne devraient pas s'en soucier. Mais il peut y avoir des moments où il s'agit d'un comportement testable, même si les vrais consommateurs ne s'en soucient jamais. Considérons une usine censée créer une iconnection (ou une iconnecturefactory), étant donné une chaîne de configuration. Je voudrais m'assurer que cela me donnait le bon type de connexion. Le passage d'une connexion de pipe init sur un objet HTTPS ne va pas faire beaucoup de bien. Si l'usine vous donne le mauvais type, tous les tests passeraient, mais vous auriez toujours un problème.
Eh bien, maintenant tu m'as fait repenser ma position légèrement. Si la spécification ne dit pas de donner une chaîne de connexion à une base de données SQL Server renvoie une instance de SQLConnection code>, vous ne pouvez pas dépendre de ce comportement et ne le testez pas pour le retourner.
iconnection code> qui vous permet de vous connecter à la base de données spécifiée dans la chaîne de connexion). Toutefois, si la spécification indique que vous récupérez un
sqlconnection code>, cela peut être dépendant et doit être testé pour. De même, si la spécification indique
FOOFACTORY.CREATE code> renvoie un
FOO code>, testez-le. Je pense que nous nous sommes rencontrés au milieu. :-)
@Jason La chose amusante à ce sujet est qu'en raison de refactoring, je viens de supprimer ce type de test de 1 des très rares endroits où j'ai eu la nécessité de le faire.
Les usines ne sont généralement pas testées par unité, au moins en fonction de mon expérience - il n'y a pas beaucoup de valeur dans l'unité les tester forte>. Parce que c'est la mise en œuvre de cette cartographie de mise en œuvre de l'interface, il fait référence à la mise en œuvre concrète. En tant que tel, la moqueur D'autre part, des conteneurs généralement diaboliques fonctionnent comme des usines, donc si vous utilisez un, vous n'auriez pas besoin d'une usine. P> FOO code> n'est pas possible de vérifier si elle reçoit ces paramètres passés. P>
Des usines abstraites sont nécessaires lorsqu'une dépendance repose sur des paramètres seulement connus au moment de l'exécution ou à la durée de vie est plus courte que celle du consommateur: Stackoverflow.com/Questtions/1993397/...
La plupart des cadres de DI feraient le passage des paramètres au moment de l'exécution. Ils peuvent également faire la gestion de la vie.
Eh bien, probablement ces paramètres rendent le renvoi ifoo code> avoir quelque chose de vrai à ce sujet. Testez pour cela être vrai sur l'instance renvoyée. p>
Cela ne signifie-t-il pas que vous auriez besoin de savoir sur les internes de l'IFOO en question?
Non, vous passez dans ces paramètres pour une raison pour obtenir un comportement attendu. Quel est ce comportement? Test pour cela. La spécification pour FOOFACTORY.CREATE code> devrait dire quelque chose comme "crée un objet qui implémente
ifoo code> tel que
p (param1, param2) code> où
P code> est un prédicat dépendant de
param1 code> et
param2 code> ". Testez que
p (param1, param2) code> est vrai pour l'objet retourné!
Donc ... supposant que FOO est non trivial et déjà couvert par N tests, vous devez essentiellement écrire n nouveaux tests presque identiques pour tester que le comportement de l'instance FOO retourné est cohérente avec l'entrée à la méthode de création. . Cela ressemble à ce que vous seriez indirectement couplant les tests. Les problèmes de maintenabilité suivront.
Voici comment j'écrirais l'un des couple de tests unitaires pour une telle usine (avec xunit.net ): fait-il enfreindre l'encapsulation? Oui et non ... un peu. L'encapsulation est non seulement sur la cache de données - plus important encore, il s'agit de Protéger les invariants d'objets . < / p> Supposons que FOO expose cette API publique: p> tandis que l'objet En outre, le < Code> Object1 Code> Une propriété fait partie de la classe FOO concret - non pas l'interface IFOO: p> une fois que vous avez réalisé cela dans une API couplée lâche, Les membresConnete sont des détails de la mise en œuvre , de telles propriétés en lecture seule concrètes ont un impact très faible sur l'encapsulation. Pensez-y de cette façon: P> Le Constructeur public public est em> une partie de l'API de la classe de béton FOO, donc simplement en inspectant l'API publique, nous apprenons que Ces propriétés fournissent l'avantage que nous pouvons maintenant tester le Forme structurelle de la classe FOO renvoyée par l'usine. P> C'est beaucoup plus facile que de répéter un ensemble de tests d'unités comportementales qui, nous devons supposer, couvrent déjà la classe FOO concrète. C'est presque comme une preuve logique: p> tandis1 code> violez légèrement le Droit de Demeter , il ne plaisante pas avec les invariants de la classe car il est en lecture seule. P>
param1 code> et
param2 code> font partie de la classe. Dans un sens, cela "casse d'encapsulation déjà", ce qui rend chaque paramètre disponible en tant que propriétés en lecture seule sur la classe de béton ne change pas beaucoup. P>
Oui, a du sens. J'aime l'idée de différencier l'API publique de l'interface et de la classe de béton.
Les tests d'unités doivent dépendre de l'exigence fonctionnelle et des attentes sur le composant testé. Je ne vois aucune valeur de l'unité testant cette classe sans le reste du contexte.
@IVOWIBLO Je pense que vous confondez des tests d'unité avec des tests de style BDD. Si vous n'écrivez pas un test pour cet appareil, comment savez-vous si cela fonctionne?
faire le test de l'unité, mais que allez-vous tester? Quelle est l'attente de cette unité? J'ai dit que je ne vois pas la valeur de faire un test d'unité de cette classe sans rien savoir d'autre. Faire des tests unitaires juste en raison de tests d'unité n'a pas de sens du tout. Des tests (unités, comportements, fonctionnalités complètes, tout ce que vous voulez) devraient toujours être motivés par des attentes et des exigences. Sinon, c'est juste snobisme (si ce mot existe).
@IVOWIBLO Le test garantit que les paramètres corrects sont transmis à la création de FOO.
Et quelle est la valeur de cela, en plus d'améliorer la couverture?
Je trouve la valeur dans l'amélioration de la couverture: en le gardant haut, il est plus facile de localiser les zones qui ont besoin de test.