Je me demande si la mesure de la couverture du code conditionnel par les outils actuels pour Java n'est pas obsolète depuis l'arrivée de Java 8. Avec Java 8 de Optional
et Stream
on peut souvent éviter les branches / boucles code, ce qui le rend facile d'obtenir une couverture très élevée conditionnelle sans tester tous les chemins d'exécution possibles. Comparons l'ancien code Java avec le code Java 8:
Avant Java 8:
public String getName(User user) {
if (user != null) {
if (user.getName() != null) {
return user.getName();
}
}
return "unknown";
}
Il existe 3 chemins d'exécution possibles dans la méthode ci-dessus. Afin d'obtenir 100% de couverture conditionnelle, nous devons créer 3 tests unitaires.
Java 8:
public String getName(User user) {
return Optional.ofNullable(user)
.map(User::getName)
.orElse("unknown");
}
Dans ce cas, les branches sont cachées et nous n'avons besoin que d'un test pour obtenir une couverture à 100% et peu importe le cas que nous testerons. Bien qu'il y ait toujours les mêmes 3 branches logiques qui devraient être couvertes je crois. Je pense que cela rend les statistiques de couverture conditionnelle totalement non fiables de nos jours.
Est-il judicieux de mesurer la couverture conditionnelle du code Java 8? Existe-t-il d'autres outils pour repérer le code sous-testé?
la source
getName
? Il semble que siuser
est nul, il devrait retourner "inconnu". Siuser
n'est pas nul etuser.getName()
est nul, il doit retourner "inconnu". Siuser
n'est pas nul etuser.getName()
n'est pas nul, il doit le renvoyer. Vous testeriez donc ces trois cas à l'unité, car c'est de cela qu'ilgetName
s'agit. Vous semblez le faire à l'envers. Vous ne voulez pas voir les succursales et écrire les tests en fonction de celles-ci, vous voulez écrire vos tests en fonction de votre contrat et vous assurer que le contrat est rempli. C'est alors que vous avez une bonne couverture.Réponses:
Je n'en connais aucun. J'ai essayé d'exécuter le code que vous avez via JaCoCo (alias EclEmma) juste pour être sûr, mais il montre 0 branches dans la
Optional
version. Je ne connais aucune méthode de configuration pour dire le contraire. Si vous le configuriez pour inclure également des fichiers JDK, il afficherait théoriquement des branchesOptional
, mais je pense qu'il serait idiot de commencer à vérifier le code JDK. Vous devez simplement supposer que c'est correct.Je pense que le problème principal, cependant, est de réaliser que les branches supplémentaires que vous aviez avant Java 8 étaient, en un sens, des branches créées artificiellement. Le fait qu'ils n'existent plus dans Java 8 signifie simplement que vous avez maintenant le bon outil pour le travail (dans ce cas,
Optional
). Dans le code pré-Java 8, vous deviez écrire des tests unitaires supplémentaires afin d'avoir la certitude que chaque branche de code se comporte de manière acceptable - et cela devient un peu plus important dans les sections de code qui ne sont pas triviales comme leUser
/getName
exemple.Dans le code Java 8, vous placez plutôt votre confiance dans le JDK que le code fonctionne correctement. En l'état, vous devez traiter cette
Optional
ligne comme les outils de couverture de code la traitent: 3 lignes avec 0 branches. Le fait qu'il y ait d'autres lignes et branches dans le code ci-dessous est quelque chose auquel vous n'avez pas prêté attention auparavant, mais qui a existé chaque fois que vous avez utilisé quelque chose comme unArrayList
ouHashMap
.la source
if
etnull
fait toujours partie du langage ;-) Il est toujours possible d'écrire du code à l'ancienne et de passernull
utilisateur ou utilisateur avecnull
nom. Vos tests doivent simplement prouver que le contrat est respecté quelle que soit la façon dont la méthode est mise en œuvre. Le fait est qu'il n'y a aucun outil pour vous dire si vous avez entièrement testé le contrat.Optional
(et aux méthodes connexes), vous n'avez plus à les tester. Pas de la même manière que vous avez testé unif-else
: toutif
était un champ de mines potentiel.Optional
et les idiomes fonctionnels similaires sont déjà codés et garantis pour ne pas vous trébucher, donc essentiellement il y a une "branche" qui a disparu.Optional
. Comme il l'a dit, logiquement, nous devrions toujours tester quigetName()
gère diverses entrées possibles de la manière que nous souhaitons, quelle que soit sa mise en œuvre. Il est plus difficile de déterminer cela sans que les outils de couverture de code ne contribuent à la manière dont ils seraient pré-JDK8.if-else
car chacune de ces constructions est complètement ad hoc. En revanche,Optional
,orElse
,map
, etc, sont tous déjà testés. Les branches, en effet, "disparaissent" lorsque vous utilisez des idiomes plus puissants.