Notre collègue estime que la rédaction de tests unitaires nous aide réellement à affiner notre travail de conception et de refactorisation, mais je ne vois pas comment. Si je charge et analyse un fichier CSV, comment le test unitaire (validation des valeurs dans les champs) va-t-il m'aider à vérifier ma conception? Il a parlé de couplage et de modularité, etc., mais pour moi cela n’a pas beaucoup de sens - mais je n’ai pas beaucoup de connaissances théoriques, cependant.
Ce n'est pas la même chose que la question que vous avez marquée comme étant en double, je serais intéressé par des exemples concrets de la façon dont cela aide, et pas seulement par la théorie disant "ça aide". J'aime la réponse ci-dessous et le commentaire, mais j'aimerais en savoir plus.
la source
Réponses:
Non seulement les tests unitaires facilitent la conception, mais c’est l’un de leurs principaux avantages.
L'écriture test-first élimine la modularité et la structure de code propre.
Lorsque vous écrivez votre test de code en premier, vous constaterez que toutes les "conditions" d'une unité de code donnée sont naturellement poussées vers des dépendances (généralement via des imitations ou des stubs) lorsque vous les supposez dans votre code.
"Étant donné la condition x, attendez le comportement y", deviendra souvent une souche à fournir
x
(ce qui est un scénario dans lequel le test doit vérifier le comportement du composant actuel) ety
deviendra une simulation, un appel qui sera vérifié à la fin du test (à moins qu'il ne s'agisse d'un "devrait retournery
", auquel cas le test vérifiera simplement la valeur de retour de manière explicite).Ensuite, une fois que cette unité se comporte comme spécifié, vous écrivez les dépendances (pour
x
ety
) que vous avez découvertes.L'écriture de code propre et modulaire est donc un processus très simple et naturel. Sinon, il est souvent facile de brouiller responsabilités et comportements de couple sans s'en rendre compte.
Écrire des tests plus tard vous dira quand votre code est mal structuré.
Lorsque rédiger des tests pour un élément de code devient difficile parce qu'il y a trop d'éléments à bloquer ou à imiter, ou parce que les éléments sont trop étroitement liés, vous savez que vous devez apporter des améliorations à votre code.
Lorsque "changer de test" devient un fardeau, car il y a tellement de comportements dans une même unité, vous savez que vous devez apporter des améliorations à votre code (ou simplement à votre approche de la rédaction de tests - mais ce n'est généralement pas le cas dans mon expérience). .
Lorsque vos scénarios deviennent trop compliquées ( « si
x
ety
etz
puis ... ») parce que vous avez besoin de plus abstrait, vous savez que vous avez des améliorations à apporter dans votre code.Lorsque vous vous retrouvez avec les mêmes tests dans deux appareils différents en raison de la duplication et de la redondance, vous savez que vous devez apporter des améliorations à votre code.
Voici une excellente conférence de Michael Feathers démontrant la relation très étroite qui existe entre testabilité et conception en code (publié à l'origine par displayName dans les commentaires). La conférence aborde également certaines des plaintes communes et des idées fausses sur la bonne conception et la testabilité en général.
la source
L'avantage des tests unitaires est qu'ils vous permettent d'utiliser votre code de la même manière que les autres programmeurs.
Si votre code est difficile à tester à l’unité, il sera probablement difficile à utiliser. Si vous ne pouvez pas injecter des dépendances sans passer par des cerceaux, votre code sera probablement inflexible. Et si vous avez besoin de passer beaucoup de temps à configurer des données ou à déterminer l'ordre dans lequel vous souhaitez effectuer certaines tâches, votre code sous test comporte probablement trop de couplages et il sera difficile de travailler avec vous.
la source
J'ai mis beaucoup de temps à comprendre, mais le véritable avantage (pour moi, votre kilométrage peut varier) du développement piloté par les tests (à l' aide de tests unitaires) est que vous devez concevoir l'API à l'avance !
Une approche typique du développement consiste d'abord à trouver le moyen de résoudre un problème donné et, avec cette connaissance et cette conception de mise en œuvre initiale, à invoquer votre solution. Cela peut donner des résultats plutôt intéressants.
Lorsque vous utilisez TDD, vous devez au tout premier écrire le code qui utilisera votre solution. Les paramètres d'entrée et la sortie attendue vous permettent de vous assurer que tout est correct. Cela vous oblige à déterminer ce dont vous avez réellement besoin pour pouvoir créer des tests significatifs. Alors et seulement alors, vous implémentez la solution. J’ai également constaté que lorsque vous savez exactement ce que votre code est censé réaliser, cela devient plus clair.
Ensuite, après la mise en œuvre, les tests unitaires vous aident à vous assurer que le refactoring ne rompt pas avec les fonctionnalités, et vous expliquent comment utiliser votre code (ce que vous savez être exact si le test a réussi!). Mais ceux-ci sont secondaires - le plus grand avantage est la mentalité dans la création du code.
la source
Je suis d'accord à 100% sur le fait que les tests unitaires nous aident "à affiner notre conception et nos éléments de refactorisation".
Je me demande bien si ils vous aident à faire la conception initiale . Oui, ils révèlent des défauts évidents et vous obligent à réfléchir à "comment puis-je rendre le code testable"? Cela devrait entraîner moins d'effets secondaires, une configuration et des paramétrages plus faciles, etc.
Cependant, selon mon expérience, des tests unitaires trop simplistes, rédigés avant que vous ne compreniez vraiment ce que la conception devrait être ((certes, c'est une exagération du TDD dur, mais trop souvent, les codeurs écrivent un test avant de trop réfléchir) - conduisent souvent à une anémie. modèles de domaine qui exposent trop d'internes.
Mon expérience avec TDD remonte à plusieurs années. Je suis donc intéressée par les nouvelles techniques qui pourraient aider à rédiger des tests qui ne biaisent pas trop la conception sous-jacente. Merci.
la source
Le test unitaire vous permet de voir le fonctionnement des interfaces entre les fonctions et vous indique souvent comment améliorer à la fois la conception locale et la conception globale. De plus, si vous développez vos tests unitaires tout en développant votre code, vous disposez d'une suite de tests de régression prête à l'emploi. Peu importe que vous développiez une interface utilisateur ou une bibliothèque d'arrière-plan.
Une fois le programme développé (avec les tests unitaires), une fois les bogues découverts, vous pouvez ajouter des tests pour confirmer que les bogues sont corrigés.
J'utilise TDD pour certains de mes projets. Je déploie beaucoup d'efforts pour créer des exemples à partir de manuels ou de papiers considérés comme corrects, et tester le code que je développe à l'aide de ces exemples. Tous les malentendus que j'ai concernant les méthodes deviennent très évidents.
J'ai tendance à être un peu plus lâche que certains de mes collègues car je me fiche de savoir si le code est écrit en premier ou si le test est écrit en premier.
la source
Lorsque vous voulez tester votre analyseur en détectant correctement la délimitation des valeurs, vous pouvez le transmettre à une ligne d'un fichier CSV. Pour que votre test soit direct et court, vous pouvez le tester avec une méthode qui accepte une ligne.
Cela vous fera automatiquement séparer la lecture des lignes de la lecture de valeurs individuelles.
À un autre niveau, vous ne souhaiterez peut-être pas placer toutes sortes de fichiers CSV physiques dans votre projet de test, mais faire quelque chose de plus lisible, il vous suffit de déclarer une grande chaîne CSV dans votre test pour en améliorer la lisibilité et le but. Cela vous amènera à découpler votre analyseur des E / S que vous feriez ailleurs.
Juste un exemple basique, commencez simplement à le pratiquer, vous sentirez la magie à un moment donné (je l’ai).
la source
En termes simples, la rédaction de tests unitaires permet d’exposer les failles de votre code.
Ce guide spectaculaire pour l'écriture de code testable , écrit par Jonathan Wolter, Russ Ruffer et Miško Hevery, contient de nombreux exemples montrant comment les failles du code, qui empêchent les tests, empêchent également la réutilisation et la flexibilité du même code. Ainsi, si votre code est testable, il est plus facile à utiliser. La plupart des "valeurs morales" sont des astuces ridiculement simples qui améliorent considérablement la conception du code ( Dependency Injection FTW).
Par exemple: Il est très difficile de tester si la méthode computeStuff fonctionne correctement lorsque le cache commence à expulser des éléments. En effet, vous devez ajouter manuellement de la merde au cache jusqu'à ce que le "bigCache" soit presque plein.
Cependant, lorsque nous utilisons l'injection de dépendance, il est beaucoup plus facile de tester si la méthode computeStuff fonctionne correctement lorsque le cache commence à expulser des éléments. Tout ce que nous faisons est de créer un test dans lequel nous appelons
new HereIUseDI(buildSmallCache());
Notice, nous avons un contrôle plus nuancé de l'objet et celui-ci rapporte des dividendes immédiatement.Des avantages similaires peuvent être obtenus lorsque notre code nécessite des données qui sont généralement stockées dans une base de données ... il vous suffit de transmettre EXACTEMENT les données dont vous avez besoin.
la source
En fonction de ce que l’on entend par «tests unitaires», je ne pense pas que les tests unitaires vraiment de bas niveau facilitent autant la conception que les tests d’ intégration de niveau légèrement supérieur - des tests qui testent un groupe d’acteurs (classes, fonctions, peu importe) dans. votre code se combine correctement pour produire toute une série de comportements souhaitables convenus entre l’équipe de développement et le responsable du produit.
Si vous pouvez écrire des tests à ces niveaux, cela vous poussera à créer un code agréable, logique, semblable à une API, qui ne nécessite pas beaucoup de dépendances insensées - le désir d’avoir une configuration de test simple vous conduira naturellement à ne pas avoir beaucoup dépendances folles ou code étroitement couplé.
Ne vous y trompez pas: les tests unitaires peuvent vous mener à une mauvaise conception, ainsi qu’à une bonne conception.. J'ai vu des développeurs prendre un peu de code qui a déjà une belle conception logique et un seul problème, le séparer et introduire davantage d'interfaces uniquement à des fins de test, ce qui rend le code moins lisible et plus difficile à modifier. , et peut-être même plus de bogues si le développeur a décidé qu’avoir beaucoup de tests unitaires de bas niveau signifiait qu’ils n’avaient pas besoin de tests de niveau supérieur. Un exemple favori particulier est un bogue que j'ai corrigé où il y avait beaucoup de code "testable" très décomposé permettant d'obtenir des informations dans et hors du presse-papiers. Tous décomposés et découplés à de très petits niveaux de détail, avec beaucoup d'interfaces, beaucoup de simulacres lors des tests et autres trucs amusants. Un seul problème - il n'y avait pas de code qui a réellement interagi avec le mécanisme de presse-papiers du système d'exploitation,
Les tests unitaires peuvent certainement orienter votre conception - mais ils ne vous guident pas automatiquement vers une bonne conception. Vous devez avoir une idée de ce qu'est un bon design qui va au-delà: "ce code est testé, donc il est testable, donc c'est bon".
Bien sûr, si vous faites partie de ceux pour qui «tests unitaires» signifie «tous les tests automatisés qui ne sont pas pilotés par l'interface utilisateur», certains de ces avertissements risquent de ne pas être aussi pertinents. Les tests d’intégration de niveau supérieur sont souvent les plus utiles pour piloter votre conception.
la source
Les tests unitaires peuvent aider à la refactorisation lorsque le nouveau code passe avec succès tous les anciens tests.
Supposons que vous ayez implémenté un bubbleort parce que vous étiez pressé et que vous ne vous préoccupiez pas des performances, mais vous souhaitez maintenant un tri rapide car les données s'allongent. Si tous les tests réussissent, les choses vont bien.
Bien sûr, les tests doivent être complets pour que cela fonctionne. Dans mon exemple, il se peut que vos tests ne couvrent pas la stabilité, car ce n’est pas un problème avec bubbleort.
la source
J'ai trouvé que les tests unitaires étaient très utiles pour faciliter la maintenance à long terme d'un projet. Lorsque je reviens sur un projet après des mois et que je ne me souviens pas beaucoup des détails, l'exécution de tests m'empêche de tout casser.
la source