web-dev-qa-db-fra.com

Pourquoi JaCoCo ne couvre-t-il pas mes instructions de changement de chaîne?

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 )

enter image description here

18
Ross Drew

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:

coverage report

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:

coverage report

24
Godin