Voici le code:
package com.XXX;
public final class Foo {
private Foo() {
// intentionally empty
}
public static int bar() {
return 1;
}
}
Voici le test:
package com.XXX;
public FooTest {
@Test
void testValidatesThatBarWorks() {
int result = Foo.bar();
assertEquals(1, result);
}
@Test(expected = java.lang.IllegalAccessException.class)
void testValidatesThatClassFooIsNotInstantiable() {
Class cls = Class.forName("com.XXX.Foo");
cls.newInstance(); // exception here
}
}
Fonctionne bien, la classe est testée. Mais Cobertura dit qu'il n'y a aucune couverture de code du constructeur privé de la classe. Comment pouvons-nous ajouter une couverture de test à un tel constructeur privé?
java
testing
code-coverage
yegor256
la source
la source
Réponses:
Eh bien, il existe des moyens d'utiliser la réflexion, etc., mais cela en vaut-il vraiment la peine? C'est un constructeur qui ne devrait jamais être appelé , non?
S'il y a une annotation ou quelque chose de similaire que vous pouvez ajouter à la classe pour faire comprendre à Cobertura qu'elle ne sera pas appelée, faites-le: je ne pense pas que cela vaille la peine de passer par des cerceaux pour ajouter une couverture artificiellement.
EDIT: S'il n'y a aucun moyen de le faire, vivez simplement avec la couverture légèrement réduite. Rappelez - vous que la couverture est censée être quelque chose qui est utile pour vous - vous devriez être en charge de l'outil, et non l'inverse.
la source
Je ne suis pas entièrement d'accord avec Jon Skeet. Je pense que si vous pouvez obtenir une victoire facile pour vous donner une couverture et éliminer le bruit dans votre rapport de couverture, alors vous devriez le faire. Dites à votre outil de couverture d'ignorer le constructeur, ou mettez l'idéalisme de côté et écrivez le test suivant et en avez terminé:
la source
constructor
? Ne devrait pasConstructor
être paramétré et non pas un type brut?constructor.isAccessible()
renvoie toujours false, même sur un constructeur public. On devrait utiliserassertTrue(Modifier.isPrivate(constructor.getModifiers()));
.Bien que ce ne soit pas nécessairement pour la couverture, j'ai créé cette méthode pour vérifier que la classe d'utilité est bien définie et pour faire un peu de couverture également.
J'ai placé le code complet et les exemples dans https://github.com/trajano/maven-jee6/tree/master/maven-jee6-test
la source
Modifier.isPrivate
commeisAccessible
c'était le castrue
pour les constructeurs privés dans certains cas (se moquer des interférences de la bibliothèque?).Assert.utilityClassWellDefined()
dans JUnit 4.12+. Avez-vous envisagé une pull request?setAccessible()
pour rendre le constructeur accessible pose des problèmes pour l'outil de couverture de code de Sonar (lorsque je fais cela, la classe disparaît des rapports de couverture de code de Sonar).J'avais rendu privé le constructeur de ma classe de fonctions utilitaires statiques, pour satisfaire CheckStyle. Mais comme l'affiche originale, j'ai demandé à Cobertura de se plaindre du test. Au début, j'ai essayé cette approche, mais cela n'affecte pas le rapport de couverture car le constructeur n'est jamais réellement exécuté. Donc, vraiment, tous ces tests consistent à savoir si le constructeur reste privé - et cela est rendu redondant par le contrôle d'accessibilité lors du test suivant.
Je suis allé avec la suggestion de Javid Jamae et j'ai utilisé la réflexion, mais j'ai ajouté des affirmations pour attraper quelqu'un qui joue avec la classe testée (et j'ai nommé le test pour indiquer High Levels Of Evil).
C'est tellement exagéré, mais je dois admettre que j'aime la sensation chaleureuse et floue d'une couverture de méthode à 100%.
la source
fail(...)
n'est pas nécessaire de simplement aligner avec .Avec Java 8 , il est possible de trouver une autre solution.
Je suppose que vous voulez simplement créer une classe utilitaire avec quelques méthodes statiques publiques. Si vous pouvez utiliser Java 8, vous pouvez utiliser à la
interface
place.Il n'y a aucun constructeur et aucune plainte de Cobertura. Vous devez maintenant tester uniquement les lignes qui vous intéressent vraiment.
la source
Le raisonnement derrière le test de code qui ne fait rien est d'atteindre une couverture de code à 100% et de remarquer quand la couverture de code diminue. Sinon, on pourrait toujours penser, hé je n'ai plus de couverture de code à 100% mais c'est PROBABLEMENT à cause de mes constructeurs privés. Cela permet de repérer facilement les méthodes non testées sans avoir à vérifier qu'il s'agissait simplement d'un constructeur privé. Au fur et à mesure que votre base de code grandit, vous ressentirez une agréable sensation de chaleur en regardant à 100% au lieu de 99%.
IMO, il est préférable d'utiliser la réflexion ici, car sinon, vous devrez soit obtenir un meilleur outil de couverture de code qui ignore ces constructeurs, soit dire à l'outil de couverture de code d'ignorer la méthode (peut-être une annotation ou un fichier de configuration) car vous seriez alors bloqué avec un outil de couverture de code spécifique.
Dans un monde parfait, tous les outils de couverture de code ignoreraient les constructeurs privés qui appartiennent à une classe finale car le constructeur est là comme une mesure de «sécurité» rien d'autre :)
Et puis ajoutez simplement des classes au tableau au fur et à mesure.J'utiliserais ce code:
la source
Les nouvelles versions de Cobertura ont un support intégré pour ignorer les getters / setters / constructeurs triviaux:
https://github.com/cobertura/cobertura/wiki/Ant-Task-Reference#ignore-trivial
Ignorer Trivial
Ignorer trivial permet d'exclure les constructeurs / méthodes qui contiennent une ligne de code. Certains exemples incluent un appel à un super constructeur uniquement, des méthodes getter / setter, etc. Pour inclure l'argument ignorer trivial, ajoutez ce qui suit:
ou dans une version Gradle:
la source
Ne fais pas ça. Quel est l'intérêt de tester un constructeur vide? Depuis cobertura 2.0, il existe une option pour ignorer ces cas triviaux (avec les setters / getters), vous pouvez l'activer dans maven en ajoutant une section de configuration au plugin cobertura maven:
Vous pouvez également utiliser les annotations de couverture :
@CoverageIgnore
.la source
Enfin, il y a une solution!
la source
Enum<E>
soit vraiment une énumération ... Je crois que cela révèle mieux l'intention.Je ne sais pas pour Cobertura mais j'utilise Clover et il a un moyen d'ajouter des exclusions de correspondance de motifs. Par exemple, j'ai des modèles qui excluent les lignes de journalisation apache-commons afin qu'elles ne soient pas comptées dans la couverture.
la source
Une autre option consiste à créer un initialiseur statique similaire au code suivant
De cette façon, le constructeur privé est considéré comme testé et la surcharge d'exécution n'est fondamentalement pas mesurable. Je fais cela pour obtenir une couverture à 100% en utilisant EclEmma, mais cela fonctionne probablement pour chaque outil de couverture. L'inconvénient de cette solution, bien sûr, est que vous écrivez du code de production (l'initialiseur statique) uniquement à des fins de test.
la source
ClassUnderTest testClass = Whitebox.invokeConstructor (ClassUnderTest.class);
la source
Parfois, Cobertura marque le code non destiné à être exécuté comme «non couvert», il n'y a rien de mal à cela. Pourquoi vous souciez-vous d'avoir une
99%
couverture au lieu de100%
?Techniquement, cependant, vous pouvez toujours invoquer ce constructeur avec réflexion, mais cela me semble très faux (dans ce cas).
la source
Si je devais deviner l'intention de votre question, je dirais:
Pour 1, il est évident que vous souhaitez que toutes les initialisations soient effectuées via des méthodes d'usine. Dans de tels cas, vos tests devraient pouvoir tester les effets secondaires du constructeur. Cela devrait entrer dans la catégorie des tests de méthodes privées normales. Réduisez la taille des méthodes afin qu'elles ne fassent qu'un nombre limité de choses déterminées (idéalement, juste une chose et une chose bien), puis testez les méthodes qui reposent sur elles.
Par exemple, si mon constructeur [privé] configure les champs d'instance de ma classe
a
sur5
. Ensuite, je peux (ou plutôt dois) le tester:Pour 2, vous pouvez configurer clover pour exclure les constructeurs Util si vous avez un modèle de dénomination défini pour les classes Util. Par exemple, dans mon propre projet, j'utilise quelque chose comme ceci (parce que nous suivons la convention selon laquelle les noms de toutes les classes Util doivent se terminer par Util):
J'ai délibérément laissé de côté un
.*
suivant)
parce que de tels constructeurs ne sont pas destinés à lancer des exceptions (ils ne sont pas destinés à faire quoi que ce soit).Il peut bien sûr y avoir un troisième cas où vous voudrez peut-être avoir un constructeur vide pour une classe non utilitaire. Dans de tels cas, je vous recommande de mettre un
methodContext
avec la signature exacte du constructeur.Si vous avez beaucoup de classes exceptionnelles, vous pouvez choisir de modifier le constructeur privé généralisé reg-ex que j'ai suggéré et de le supprimer
Util
. Dans ce cas, vous devrez vous assurer manuellement que les effets secondaires de votre constructeur sont toujours testés et couverts par d'autres méthodes dans votre classe / projet.la source
Test.java est votre fichier source, qui a votre constructeur privé
la source
Ce qui suit m'a fonctionné sur une classe créée avec l'annotation Lombok @UtilityClass, qui ajoute automatiquement un constructeur privé.
Bien que constructor.setAccessible (true) devrait fonctionner lorsque le constructeur privé a été écrit manuellement, avec Lombok, l'annotation ne fonctionne pas, car elle le force. Constructor.newInstance () teste en fait que le constructeur est appelé et cela complète la couverture du costructeur lui-même. Avec l'assertThrows, vous évitez que le test échoue et vous avez géré l'exception car c'est exactement l'erreur que vous attendez. Bien que ce soit une solution de contournement et que je n'apprécie pas le concept de «couverture de ligne» par rapport à «couverture de fonctionnalité / comportement», nous pouvons trouver un sens à ce test. En fait, vous êtes sûr que la classe utilitaire a en fait un constructeur privé qui lève correctement une exception lorsqu'il est appelé également via reflaction. J'espère que cela t'aides.
la source
Mon option préférée en 2019: utiliser lombok.
Plus précisément, l'
@UtilityClass
annotation . (Malheureusement seulement "expérimental" au moment de l'écriture, mais il fonctionne très bien et a une perspective positive, donc susceptible d'être bientôt mis à niveau vers stable.)Cette annotation ajoutera le constructeur privé pour empêcher l'instanciation et rendra la classe finale. Lorsqu'ils sont combinés avec
lombok.addLombokGeneratedAnnotation = true
inlombok.config
, pratiquement tous les frameworks de test ignoreront le code généré automatiquement lors du calcul de la couverture de test, vous permettant de contourner la couverture de ce code généré automatiquement sans piratage ni réflexion.la source
Vous ne pouvez pas.
Vous créez apparemment le constructeur privé pour empêcher l'instanciation d'une classe destinée à ne contenir que des méthodes statiques. Plutôt que d'essayer d'obtenir une couverture de ce constructeur (ce qui exigerait que la classe soit instanciée), vous devriez vous en débarrasser et faire confiance à vos développeurs pour ne pas ajouter de méthodes d'instance à la classe.
la source