Je me demande comment je devrais tester ce type de fonctionnalité via Nunit.
Public void HighlyComplexCalculationOnAListOfHairyObjects() { // calls 19 private methods totalling ~1000 lines code + comments + whitespace }
9 Réponses :
Pour résoudre votre problème immédiat, vous voudrez peut-être jeter un coup d'œil à PEX < / a>, qui est un outil de la recherche Microsoft qui répond à ce type de problème en trouvant toutes les valeurs de limites pertinentes afin que tous les chemins de code puissent être exécutés. P>
Cela dit, aviez-vous utilisé le développement axé sur les tests (TDD), vous ne vous aviez jamais trouvé dans cette situation, car il aurait été quasi impossible pour écrire des tests d'unité qui conduisent ce type d'API. P>
Une méthode comme celle que vous décrivez des sons comme il essaie de faire trop de choses à la fois. L'un des principaux avantages de TDD est qu'il vous permet de mettre en œuvre votre code à partir de petits objets composables au lieu de grandes classes avec des interfaces inflexibles. P>
+1 pour TDD. Comme Mark le dit, écrivez d'abord des tests et vous ne vous retrouverez pas ici.
L'eau sous le pont. Le code en question a été écrit bien avant qu'il y ait un intérêt officiel dans une sorte de test automatisé de l'équipe.
PEX a l'air intéressant mais la version d'évaluation commerciale requiert la version de l'équipe d'édition de VS et tout le monde sur mon équipe n'a que 2K8PRO.
Vous avez enflé deux choses. L'interface (qui pourrait exposer très peu) et cette classe de mise en œuvre particulière qui pourrait exposer beaucoup plus. P>
définir l'interface la plus étroite possible. P> li>
Définissez la classe de mise en œuvre avec des méthodes et des attributs testables (non privés). Ça va bien si la classe a des choses "supplémentaires". P> li>
Toutes les applications doivent utiliser l'interface et, par conséquent, n'ont pas d'accès de type à sécurité aux fonctionnalités exposées de la classe. P> li> ol>
Et si "quelqu'un" contourne l'interface et utilise directement la classe? Ils sont des sociopathes - vous pouvez les ignorer en toute sécurité. Ne leur fournissez pas de support téléphonique car ils ont violé la règle fondamentale d'utiliser l'interface et non de la mise en œuvre. p>
Exposer des méthodes pour faciliter les tests ne se sent pas bien.
@Dan NEELY: (1) C'est pourquoi c'est le test conçu i>. Vous avez à peu près à. Plus important. (2) C'est pourquoi l'interface et la mise en œuvre sont séparées. L'interface n'expose rien. Et (3) dans Python, nous ne dérangons pas beaucoup avec "Private" et nous sommes parfaitement heureux et réussis. "Exposition" ne pas - à long terme - comptez beaucoup.
@S. Lott - une question, comment cela aide-t-il si vos méthodes précédemment appellent d'autres méthodes privées? Même si vous faites tous ces publics, vous n'avez pas d'interface pour eux, vous ne pouvez donc pas les moquer (sauf si vous les faites aussi virtuelle, ce qui semble un peu étrange). Des pensées?
La mise en œuvre (pas l'interface) n'a peu d'utilisations pour les méthodes privées. En outre, la mise en œuvre présente également des méthodes qui sont l'API «officielle» - la partie testable - distincte de plus de détails sur la mise en œuvre privés. Vous n'avez pas à tester chaque méthode i>. Il vous suffit de tester les méthodes qui sont l'interface "officielle". Si vous ne pouvez pas distinguer, supprimez-vous avec privé code> et utilisez l'interface pour fournir toute la confidentialité dont vous avez vraiment besoin.
J'ai vu (et probablement écrit) beaucoup d'objets de cheveux. S'il est difficile de tester, c'est généralement un bon candidat pour refactoring. Bien sûr, un problème avec c'est que la première étape de refactoring est en train de s'assurer qu'elle passe d'abord tous les tests. P>
Honnêtement, cependant, je chercherais à voir s'il n'y a pas une manière dont vous pouvez casser ce code dans une section plus gérable. p>
Votre question implique qu'il existe de nombreux chemins d'exécution dans tout le sous-système. La première idée qui apparaît à l'esprit est "Refactor". Même si votre API reste une interface à une méthode, les tests ne doivent pas être «impossibles». P>
Personnellement, je ferais personnellement les méthodes constitutives internes, appliquer Les tests d'unités de boîte blanche peuvent toujours être efficaces - bien que cela soit généralement plus fragile que le test de boîte noire (c'est-à-dire que vous êtes plus susceptible de devoir modifier les tests si vous modifiez la mise en œuvre). P> internalsvisibleto code> et tester les différents bits. P>
Le code a été autour et dans la production qui sauf des bugs découverts que le changement est peu probable à ce stade. Au niveau de l'ondulation des mains, l'algorithme fait quelque chose de similaire à la compression de la perte en fonction de la mise en œuvre, il existe plus d'une sortie possible pour une entrée donnée qui serait «correcte» et que tout changement majeur. En conséquence, tous les changements majeurs briseraient probablement les tests Blackbox.
Un problème à un tel refactoring est que la classe d'origine a tenu beaucoup d'état que la nouvelle classe aurait besoin. Qui est une autre odeur de code, une personne qui indique que l'état doit être déplacé dans un objet de valeur. P> HautComplexCalcultonalistofhairyObjects () code> est une odeur de code, une indication que la classe qui contient cela est potentiellement trop nombreuse et doit être refactored via Classe d'extraction em>. Les méthodes de cette nouvelle classe seraient publiques et donc testables comme des unités. P>
Comme mentionné, Une autre chose serait de refactoriser la grande méthode en classes plus petites et plus définies. Vérifiez cette question que j'ai posée sur un problème similaire, Test de grandes méthodes . P> interneVissibleto ("montageName") code> est un bon endroit pour commencer lors du test du code hérité. p>
Les méthodes internes code> sont toujours privées en ce sens que les assemblages en dehors de l'assemblage actuel ne peuvent pas voir les méthodes. Vérifiez MSDN pour plus d'infomation. P>
essayer de créer un ensemble de données de test qui a pleinement exécuté tout le fonctionnalité impliquée dans le le calcul serait presque impossible p> blockQuote>
Si c'est vrai, essayez un objectif moins ambitieux. Commencez par tester des chemins spécifiques à l'utilisation élevée grâce au code, les chemins que vous soupçonnez peuvent être fragiles et des chemins pour lesquels vous avez signalé des bugs. P>
refactoring La méthode dans des sous-algorithmes distincts rendra votre code plus testable (et pourrait être bénéfique d'une autre manière), mais si votre problème est un nombre ridicule d'interactions forts> entre ces sous-algorithmes, La méthode d'extraction (ou extraire de la classe de stratégie) ne le résoudra pas vraiment: vous devrez construire une suite solide de tests une à la fois. P>
Dans une certaine mesure, nous l'avons déjà. Tous les cas communs sont couverts par un ensemble de fichiers de test pour un test d'acceptation axé sur l'homme; Et l'un de mes collègues est (AB) en utilisant Nunit pour créer un ensemble de tests de niveau d'intégration qui chargent, traite et examine la sortie des fichiers de test. En raison de la complexité de ce qui se passe à l'intérieur de la poussée principale consiste à tester les méthodes internes séparément pour vous assurer que nous n'avons pas manqué de cas de bord. Pour mes péchés (avoir fait le VBA-> C # portuaire il y a des années), et avoir la meilleure idée de ce qui se passe à l'intérieur de la deuxième partie est tombé à moi.
Deuxièmement. C'est une excellente ressource pour la construction des îles de code testé de manière non testée et apparemment plus abandonnée. Une fois que vous avez suffisamment de tests en place, vous serez en mesure de résoudre et de refroidir la base de code existante.
Comme d'autres l'ont souligné: si une classe est si difficile à tester, c'est souvent un signe que la classe tente de faire trop. Si le calcul peut être décomposé en méthodes plus petites, est-il possible de la rompre plus loin dans des étapes séparées, à chaque étape représentée par sa propre classe / interface? Ou, si le calcul consiste principalement en un groupe de formules complexes, vous pouvez avoir une classe spécialisée
maths code> -itant avec un tas de méthodes statiques mettant en œuvre chaque formule.
Non sans un degré de ré-factorisation qui est en dehors de la portée / budget / temps actuel.
Dan - Qu'avez-vous fini par faire? Nous avons les mêmes problèmes.
J'ai fini par liaison au cadre de test Visual Studio et en ajoutant "Utilisation de SivniteObject = Microsoft.VisualStudio.TestTools.UnittestSting.privateObject;" à mon projet de test afin que je puisse accéder aux membres privés sans écrire ma propre classe de réflexion à le faire. J'ai ensuite ajouté un certain nombre de tests de méthodes privées à bas niveau au bas de l'arborescence appelant pour remplir les niveaux de niveau élevé en exécutant la méthode publique de niveau supérieur. À l'heure actuelle, il n'y a pas de test qui couvre explicitement les méthodes de niveau moyen. Je pourrais les ajouter plus tard, mais la quantité de configuration nécessaire pour créer des intrants est prohibitive.
Comme indiqué ci-dessous, ce n'est pas l'option idéale, mais des fonds pour une refonte majeure n'existent pas, donc je fais ce que je peux maintenant. Les améliorations futures des tests seront envisagées lorsque des fonds supplémentaires sont disponibles ou en réponse à tout problème spécifique identifié.