web-dev-qa-db-fra.com

JUnit: test de la classe d'assistance avec uniquement des méthodes statiques

Je teste une classe d'assistance avec uniquement des méthodes statiques avec JUnit4 et Cobertura. Tester les méthodes était une tâche facile et est déjà fait.

Cependant, cobertura montre que la classe n'est pas complètement couverte par les tests, car elle n'est instanciée nulle part.

Je ne veux pas créer une instance de cette classe (c'est une classe d'assistance), donc la première solution est de cacher le constructeur (ce qui est généralement une bonne approche pour la classe d'assistance).

Ensuite, cobertura se plaint que le constructeur privé vide n'est pas couvert par des tests.

Existe-t-il une solution pour atteindre une couverture de code à 100% pour une telle situation?

La couverture du code est exigée de la direction de haut niveau (dans ce cas), donc pour moi, obtenir 100% pour cette classe particulière est très utile.

32

Il existe plusieurs solutions:

  1. Vous pouvez ajouter un constructeur public et l'appeler à partir d'un test. Même si cela n'a pas de sens, cela ne fait pas de mal (beaucoup).

  2. Créez une instance statique factice (vous pouvez appeler le constructeur privé ici). Moche mais vous pouvez donner un nom au champ pour communiquer votre intention (JUST_TO_SILENCE_COBERTURA est un bon nom).

  3. Vous pouvez laisser votre test étendre la classe d'assistance. Cela appellera intrinsèquement le constructeur par défaut mais votre classe d'assistance ne peut plus être final.

Je suggère la dernière approche, surtout parce que la classe ne peut plus être final. Si un consommateur de votre code souhaite ajouter une autre méthode d'assistance, il peut désormais étendre la classe existante et recevoir un descripteur pour accéder à toutes les méthodes d'assistance. Cela crée un couplage des méthodes d'assistance qui communique l'intention (elles vont ensemble) - ce qui est impossible si la classe d'assistance est final

Si vous souhaitez empêcher les utilisateurs d'instancier accidentellement la classe d'assistance, faites-la abstract au lieu d'utiliser un constructeur masqué.

31
Aaron Digulla

Si vous avez absolument besoin d'obtenir une couverture de code à 100% - le bien-fondé de cela peut être débattu ailleurs :) - vous pouvez y parvenir en utilisant la réflexion dans vos tests. Comme d'habitude, lorsque j'implémente une classe utilitaire statique uniquement, j'ajoute un constructeur privé pour garantir que les instances de la classe ne peuvent pas être créées. Par exemple:

/** 
 * Constructs a new MyUtilities.
 * @throws InstantiationException
 */
private MyUtilities() throws InstantiationException
{
    throw new InstantiationException("Instances of this type are forbidden.");
}

Votre test pourrait alors ressembler à ceci:

@Test
public void Test_Constructor_Throws_Exception() throws IllegalAccessException, InstantiationException {
    final Class<?> cls = MyUtilties.class;
    final Constructor<?> c = cls.getDeclaredConstructors()[0];
    c.setAccessible(true);

    Throwable targetException = null;
    try {
        c.newInstance((Object[])null);
    } catch (InvocationTargetException ite) {
        targetException = ite.getTargetException();
    }

    assertNotNull(targetException);
    assertEquals(targetException.getClass(), InstantiationException.class);
}

Fondamentalement, ce que vous faites ici est d'obtenir la classe par son nom, de trouver les constructeurs sur ce type de classe, de le définir sur public (l'appel setAccessible), d'appeler le constructeur sans argument, puis de vous assurer que le l'exception cible levée est un InstantiationException.

Quoi qu'il en soit, comme vous l'avez dit, l'exigence de couverture de code à 100% ici est une sorte de douleur, mais il semble que ce soit hors de vos mains, donc vous ne pouvez pas y faire grand-chose. J'ai en fait utilisé des approches similaires à celles ci-dessus dans mon propre code, et je l'ai trouvé bénéfique, mais pas du point de vue des tests. Au contraire, cela m'a aidé à en apprendre un peu plus sur la réflexion que je ne le savais auparavant :)

29
matt

Obtenir une couverture à 100% dans tous les cas, c'est bien, mais il y a des cas où cela n'est pas possible. Bien sûr, si vous avez une classe qui n'est jamais instanciée, Cobertura obtiendra cela comme une couverture de test non complète, car ces lignes de code sont en fait dans la classe, mais elles ne sont pas testées.

Le fait est que vous n'appellerez jamais un constructeur privé (je suppose que vous avez caché le constructeur en le rendant privé), donc je ne m'embêterais pas. Le test devrait viser à obtenir ce que vous attendez, et bien que je convienne que la couverture à 100% est bonne, dans certains cas (comme celui-ci), cela n'est pas utile.

Jetez également un œil à couverture de code à 100% .

7
manub

Non.

À moins d'appeler explicitement le constructeur privé (ce qui serait du mauvais code), vous ne pourrez pas couvrir ces lignes.

2
oers