Sommes-nous censés écrire des tests pour nos getters et setters ou est-ce exagéré?
unit-testing
Hector421
la source
la source
Réponses:
Je dirais non.
@Will a dit que vous devriez viser une couverture de code à 100%, mais à mon avis, c'est une distraction dangereuse. Vous pouvez écrire des tests unitaires qui ont une couverture de 100%, sans rien tester.
Les tests unitaires sont là pour tester le comportement de votre code, de manière expressive et significative, et les getters / setters ne sont qu'un moyen pour atteindre une fin. Si vos tests utilisent les getters / setters pour atteindre leur objectif de tester la fonctionnalité «réelle», alors c'est assez bien.
Si, d'un autre côté, vos getters et setters font plus que simplement obtenir et définir (c'est-à-dire que ce sont des méthodes bien complexes), alors oui, ils devraient être testés. Mais n'écrivez pas un cas de test unitaire juste pour tester un getter ou des setters, c'est une perte de temps.
la source
Roy Osherove dans son célèbre livre `` The Art Of Unit Testing '' dit:
la source
Un OUI retentissant avec TDD
Remarque : cette réponse continue de recevoir des votes positifs, bien que potentiellement un mauvais conseil. Pour comprendre pourquoi, jetez un œil à sa petite sœur ci-dessous.
Bien controversé, mais je dirais que quiconque répond «non» à cette question manque un concept fondamental du TDD.
Pour moi, la réponse est un oui retentissant si vous suivez TDD. Si ce n'est pas le cas, non est une réponse plausible.
Le DDD dans TDD
Le TDD est souvent cité comme présentant les principaux avantages.
Séparer la responsabilité de la mise en œuvre
En tant que programmeurs, il est terriblement tentant de considérer les attributs comme quelque chose d'important et les getters et setter comme une sorte de surcharge.
Mais les attributs sont un détail d'implémentation, tandis que les setters et les getters sont l'interface contractuelle qui fait réellement fonctionner les programmes.
Il est bien plus important d'épeler qu'un objet doit:
et
puis comment cet état est réellement stocké (pour lequel un attribut est le moyen le plus courant, mais pas le seul).
Un test tel que
est important pour la partie documentation de TDD.
Le fait que l'implémentation éventuelle soit triviale (attribut) et ne comporte aucun avantage de défense devrait vous être inconnu lorsque vous écrivez le test.
Le manque d'ingénierie aller-retour ...
L'un des problèmes clés dans le monde du développement de systèmes est le manque d' ingénierie aller-retour 1 - le processus de développement d'un système est fragmenté en sous-processus disjoints dont les artefacts (documentation, code) sont souvent incohérents.
1 Brodie, Michael L. "John Mylopoulos: coudre des graines de modélisation conceptuelle." Modélisation conceptuelle: fondations et applications. Springer Berlin Heidelberg, 2009. 1-9.
... et comment TDD le résout
C'est la partie documentation de TDD qui garantit que les spécifications du système et son code sont toujours cohérents.
Concevez d'abord, implémentez plus tard
Dans TDD, nous écrivons d'abord les tests d'acceptation échoués, puis nous écrivons ensuite le code qui les laisse passer.
Dans le BDD de niveau supérieur, nous écrivons d'abord des scénarios, puis nous les faisons passer.
Pourquoi devriez-vous exclure les setters et les getter?
En théorie, il est parfaitement possible dans TDD qu'une personne écrive le test et qu'une autre implémente le code qui le fait passer.
Alors demandez-vous:
Puisque les getters et les setters sont une interface publique vers une classe, la réponse est évidemment oui , ou il n'y aura aucun moyen de définir ou d'interroger l'état d'un objet. Cependant , la façon de le faire n'est pas nécessairement de tester chaque méthode isolément, voir mon autre réponse pour en savoir plus.
De toute évidence, si vous écrivez d'abord le code, la réponse n'est peut-être pas aussi claire.
la source
tl; dr: Oui, vous devriez , et avec OpenPojo, c'est trivial.
Vous devriez faire une validation dans vos getters et setters donc vous devriez tester cela. Par exemple,
setMom(Person p)
ne devrait pas permettre de placer quelqu'un plus jeune qu'eux comme mère.Même si vous ne faites rien de tout cela maintenant, il y a de fortes chances que vous le fassiez à l'avenir, alors ce sera un bon pour l'analyse de régression. Si vous voulez autoriser les mères à
null
vous faire un test, si quelqu'un change cela plus tard, cela renforcera vos hypothèses.Un bogue commun est
void setFoo( Object foo ){ foo = foo; }
là où il devrait êtrevoid setFoo( Object foo ){ this.foo = foo; }
. (Dans le premier cas, le paramètre dansfoo
lequel on écrit est le paramètre et non lefoo
champ de l' objet ).Si vous renvoyez un tableau ou une collection, vous devriez tester si le getter va effectuer ou non des copies défensives des données passées au setter avant de retourner.
Sinon, si vous avez les setters / getters les plus basiques, alors les tests unitaires ajouteront peut-être environ 10 minutes au maximum par objet, alors quelle est la perte? Si vous ajoutez un comportement, vous avez déjà un test de squelette et vous obtenez ce test de régression gratuitement. Si vous utilisez Java, vous n'avez aucune excuse puisqu'il existe OpenPojo . Il existe un ensemble de règles que vous pouvez activer, puis analyser l'ensemble de votre projet avec elles pour vous assurer qu'elles sont appliquées de manière cohérente dans votre code.
D'après leurs exemples :
la source
Oui, mais pas toujours de manière isolée
Permettez-moi d'élaborer:
Qu'est-ce qu'un test unitaire?
De Travailler efficacement avec l'ancien code 1 :
Notez qu'avec la POO, où vous trouvez des getters et des setters, l'unité est la classe , pas nécessairement les méthodes individuelles .
Qu'est-ce qu'un bon test?
Toutes les exigences et tous les tests suivent la forme de la logique Hoare :
Où:
{P}
est la condition préalable ( donnée )C
est la condition de déclenchement ( quand ){Q}
est la postcondition ( alors )Puis vient la maxime:
Cela signifie que vous ne devez pas tester comment
C
réalise la post-condition, vous devez vérifier que{Q}
c'est le résultat deC
.Quand il s'agit de POO,
C
c'est une classe. Vous ne devriez donc pas tester les effets internes, uniquement les effets externes.Pourquoi ne pas tester les getters et setters de haricots isolément
Les getters et les setters peuvent impliquer une certaine logique, mais tant que cette logique n'a pas d'effet externe - ce qui en fait des accesseurs de haricots 2 ) un test devra regarder à l'intérieur de l'objet et par cela non seulement violer l'encapsulation mais aussi tester l'implémentation.
Vous ne devriez donc pas tester les getters et les setters de haricots de manière isolée. C'est mauvais:
Même si
setVAT
cela lèverait une exception, un test correspondant serait approprié car il y a maintenant un effet externe.Comment tester les getters et les setters?
Il est pratiquement inutile de changer l'état interne d'un objet si un tel changement n'a aucun effet sur l'extérieur, même si cet effet survient plus tard.
Un test pour les setters et les getters devrait donc porter sur l'effet externe de ces méthodes, pas sur l'effet interne.
Par exemple:
Vous pensez peut-être:
Mais en cas de
setVAT()
dysfonctionnement, ce test échoue tout de même.1 Feathers, M., 2004. Travailler efficacement avec le code hérité. Prentice Hall Professional.
2 Martin, RC, 2009. Clean code: un manuel de l'artisanat logiciel agile. Pearson Education.
la source
Bien qu'il existe des raisons justifiées pour les propriétés, il existe une croyance commune de conception orientée objet selon laquelle exposer l'état membre via Propriétés est une mauvaise conception. L'article de Robert Martin sur le principe ouvert fermé développe cela en déclarant que les propriétés encouragent le couplage et limitent par conséquent la possibilité de fermer une classe de la modification - si vous modifiez la propriété, tous les consommateurs de la classe devront également changer. Il précise que l'exposition des variables membres n'est pas nécessairement une mauvaise conception, mais peut-être simplement un style médiocre. Cependant, si les propriétés sont en lecture seule, il y a moins de risques d'abus et d'effets secondaires.
La meilleure approche que je puisse fournir pour les tests unitaires (et cela peut sembler étrange) est de rendre autant de propriétés que possible protégées ou internes. Cela empêchera le couplage tout en décourageant d'écrire des tests idiots pour les getters et les setters.
Il existe des raisons évidentes pour lesquelles des propriétés de lecture / écriture doivent être utilisées, telles que les propriétés ViewModel liées aux champs d'entrée, etc.
Plus concrètement, les tests unitaires devraient piloter les fonctionnalités via des méthodes publiques. Si le code que vous testez utilise ces propriétés, vous bénéficiez d'une couverture de code gratuite. S'il s'avère que ces propriétés ne sont jamais mises en évidence par la couverture de code, il y a une très forte possibilité que:
Si vous écrivez des tests pour les getters et les setters, vous obtenez une fausse impression de couverture et vous ne pourrez pas déterminer si les propriétés sont réellement utilisées par le comportement fonctionnel.
la source
Si la complexité cyclomatique du getter et / ou du setter est 1 (ce qu'ils sont généralement), alors la réponse est non, vous ne devriez pas.
Donc, à moins que vous n'ayez un SLA qui nécessite une couverture de code à 100%, ne vous inquiétez pas et concentrez-vous sur le test de l'aspect important de votre logiciel.
PS N'oubliez pas de différencier les getters et les setters, même dans des langages comme C # où les propriétés peuvent ressembler à la même chose. La complexité du setter peut être supérieure à celle du getter, et valider ainsi un test unitaire.
la source
Une prise humoristique, mais sage: The Way of Testivus
"Ecrivez le test que vous pouvez aujourd'hui"
Tester des getters / setters peut être exagéré si vous êtes un testeur expérimenté et qu'il s'agit d'un petit projet. Cependant, si vous commencez tout juste à apprendre à tester les unités ou que ces getters / setters peuvent contenir de la logique (comme l'
setMom()
exemple de @ ArtB ), alors ce serait une bonne idée d'écrire des tests.la source
C'est en fait un sujet récent entre mon équipe et moi. Nous visons une couverture de code à 80%. Mon équipe soutient que les getters et les setters sont automatiquement implémentés et que le compilateur génère du code de base dans les coulisses. Dans ce cas, étant donné que le code généré n'est pas intrusif, il n'a pas vraiment de sens de tester le code que le compilateur crée pour vous. Nous avons également eu cette discussion sur les méthodes asynchrones et dans ce cas, le compilateur génère tout un tas de code dans les coulisses. C'est un cas différent et quelque chose que nous testons. Réponse longue courte, abordez-la avec votre équipe et décidez vous-même si cela vaut la peine d'être testé.
De plus, si vous utilisez le rapport de couverture de code comme nous, une chose que vous pouvez faire est d'ajouter l'attribut [ExcludeFromCodeCoverage]. Notre solution a été de l'utiliser pour les modèles qui n'ont que des propriétés utilisant des getters et des setters, ou sur la propriété elle-même. De cette façon, cela n'affectera pas le% de couverture totale du code lorsque le rapport de couverture de code est exécuté, en supposant que c'est ce que vous utilisez pour calculer vos pourcentages de couverture de code. Bon test!
la source
À mon avis, la couverture du code est un bon moyen de voir si vous avez manqué une fonctionnalité que vous devriez couvrir.
Lorsque vous inspectez la couverture manuellement par sa jolie coloration, on peut affirmer que les getters et les setters simples n'ont pas besoin d'être testés (bien que je le fasse toujours).
Lorsque vous ne vérifiez que le pourcentage de couverture de code sur votre projet, un pourcentage de couverture de test comme 80% n'a pas de sens. Vous pouvez tester toutes les parties non logiques et oublier certaines parties cruciales. Dans ce cas, seulement 100% signifie que vous avez testé tout votre code vital (et tout le code non logique également). Dès qu'il est à 99,9%, vous savez que vous avez oublié quelque chose.
Soit dit en passant: la couverture du code est la dernière vérification pour voir si vous avez entièrement testé (à l'unité) une classe. Mais une couverture de code à 100% ne signifie pas nécessairement que vous avez réellement testé toutes les fonctionnalités de la classe. Les tests unitaires doivent donc toujours être implémentés en suivant la logique de la classe. En fin de compte, vous exécutez une couverture pour voir si vous avez oublié quelque chose. Quand vous l'avez bien fait, vous avez atteint 100% la première fois.
Encore une chose: en travaillant récemment dans une grande banque aux Pays-Bas, j'ai remarqué que Sonar indiquait une couverture de code à 100%. Cependant, je savais qu'il manquait quelque chose. En inspectant les pourcentages de couverture de code par fichier, il a indiqué un fichier à un pourcentage inférieur. L'ensemble du pourcentage de base du code était si grand que le seul fichier ne permettait pas d'afficher le pourcentage à 99,9%. Vous voudrez peut-être faire attention à cela ...
la source
J'ai fait une petite analyse de la couverture obtenue dans le code JUnit lui-même .
Une catégorie de code non couvert est «trop simple à tester» . Cela inclut de simples getters et setters, que les développeurs de JUnit ne testent pas .
D'autre part, JUnit n'a aucune méthode (non obsolète) de plus de 3 lignes qui n'est couverte par aucun test.
la source
Je dirais: OUI Les erreurs dans les méthodes getter / setter peuvent se faufiler silencieusement et provoquer des bugs horribles.
J'ai écrit une bibliothèque pour faciliter cela et d'autres tests. La seule chose que vous devez écrire dans vos tests JUnit est la suivante:
-> https://github.com/Mixermachine/base-test
la source
Oui, surtout si l'élément à obtenir est un objet d'une classe sous-classée à partir d'une classe abstraite. Votre IDE peut ou non vous alerter qu'une certaine propriété n'a pas été initialisée.
Et puis certains tests apparemment sans rapport avec a
NullPointerException
et il vous faut un certain temps pour comprendre qu'une propriété gettable n'est en fait pas là pour arriver en premier lieu.Bien que ce ne soit toujours pas aussi grave que de découvrir le problème de la production.
Ce peut être une bonne idée de vous assurer que toutes vos classes abstraites ont des constructeurs. Sinon, le test d'un getter peut vous alerter d'un problème.
En ce qui concerne les getters et les setters de primitives, la question pourrait être: est-ce que je teste mon programme ou est-ce que je teste la JVM ou le CLR? De manière générale, la JVM n'a pas besoin d'être testée.
la source