J'ai une instruction switch
qui extrait un mode d'adressage d'un String
et j'ai écrit des tests unitaires pour couvrir, ce que je pensais être toutes les éventualités mais JaCoCo semble ignorer mon switch
déclarations, résultant en une couverture plus faible.
Pourquoi, si toutes mes instructions case
, y compris une instruction par défaut, sont exécutées dans les tests, l'instruction switch
ne serait-elle pas comptée comme atteinte?
(Voir, les résultats des tests affichés dans CodeCov )
Pour le switch par String
class Fun {
static int fun(String s) {
switch (s) {
case "I":
return 1;
case "A":
return 2;
case "Z":
return 3;
case "ABS":
return 4;
case "IND":
return 5;
default:
return 6;
}
}
}
Oracle Java génère un bytecode similaire au code suivant (le compilateur Eclipse pour Java génère un bytecode légèrement différent))
int c = -1;
switch (s.hashCode()) {
case 65: // +1 branch
if (s.equals("I")) // +2 branches
c = 0;
break;
case 73: // +1 branch
if (s.equals("A")) // +2 branches
c = 1;
break;
case 90: // +1 branch
if (s.equals("Z")) // +2 branches
c = 2;
break;
case 64594: // +1 branch
if (s.equals("ABS")) // +2 branches
c = 3;
break;
case 72639: // +1 branch
if (s.equals("IND")) // +2 branches
c = 4;
break;
default: // +1 branch
}
switch (c) {
case 0: // +1 branch
return 1;
case 1: // +1 branch
return 2;
case 2: // +1 branch
return 3;
case 3: // +1 branch
return 4;
case 4: // +1 branch
return 5;
default: // +1 branch
return 6;
}
Ainsi, cette instruction switch originale avec 6 cas est représentée en bytecode par un commutateur avec 6 cas pour hashCode
de String
plus 5 instructions if plus un autre commutateur avec 6 cas. Pour voir ce bytecode, vous pouvez utiliser javap -c
.
JaCoCo effectue une analyse du bytecode et dans les versions inférieures à 0.8.0 n'a pas de filtre pour basculer par chaîne. Vos tests couvrent les cas où les conditions des instructions if sont évaluées à true
, mais pas les cas où elles sont évaluées à false
. Personnellement, je conseillerais d'ignorer simplement les cas manquants, car le but n'est pas de tester que le compilateur génère le code approprié, mais de tester que votre application se comporte correctement. Mais dans un souci d'exhaustivité de cette réponse - voici des tests qui couvrent toutes les branches de bytecode:
import org.junit.Test;
import static org.junit.Assert.*;
public class FunTest {
@Test
public void test() {
// original strings:
assertEquals(1, Fun.fun("I"));
assertEquals(2, Fun.fun("A"));
assertEquals(3, Fun.fun("Z"));
assertEquals(4, Fun.fun("ABS"));
assertEquals(5, Fun.fun("IND"));
// same hash codes, but different strings:
assertEquals(6, Fun.fun("\0I"));
assertEquals(6, Fun.fun("\0A"));
assertEquals(6, Fun.fun("\0Z"));
assertEquals(6, Fun.fun("\0ABS"));
assertEquals(6, Fun.fun("\0IND"));
// distinct hash code to cover default cases of switches
assertEquals(6, Fun.fun(""));
}
}
Et le rapport généré par JaCoCo 0.7.9 comme preuve:
JaCoCo version 0.8.0 fournit des filtres , y compris un filtre pour le bytecode que javac
produit pour le changement par chaîne. Et génère ainsi le rapport suivant même sans tests supplémentaires: