2015-07-30 9 views
9

Ich erzeuge einen jacoco-Bericht mit jacoco: report tag. Ich erhalte Fehler wie:jacoco code coverage Berichtgenerator zeigt Fehler an: "Klassen im Paket 'Code Coverage Report' stimmen nicht mit den Ausführungsdaten überein"

[jacoco:report] Classes in bundle 'Code Coverage Report' do no match with execution data. For report generation the same class files must be used as at runtime. 
[jacoco:report] Execution data for class xxxxx does not match. 
[jacoco:report] Execution data for class yyyyy does not match. 

The ant Bericht Ziel wie folgt aussieht:

<target name="report"> 
       <jacoco:report> 
         <executiondata> 
           <file file="${jacocoexec.dir}/${jacocoexec.filename}"/> 
         </executiondata> 
         <!-- the class files and optional source files ... --> 
         <structure name="Code Coverage Report"> 
           <classfiles> 
             <fileset file="./jar/abc.jar"/> 
           </classfiles> 
           <sourcefiles> 
             <fileset dir="./code/src"/> 
           </sourcefiles> 
         </structure> 
         <!-- to produce reports in different formats. --> 
         <html destdir="${jacoco.report.dir}"/> 
       </jacoco:report> 
     </target> 

Die so erzeugte durch ./code/src mit nur. Warum gibt es dann solche Fehler? Irgendeine Idee?

+0

Hinzufügen, wie Ausführungsdaten ('$ {jacocoexec.filename}') generiert werden? Es sollte ein separates Ziel geben, das die Aufgabe 'jacoco: coverage' ausführt. Stellen Sie dieses Ziel auch in Frage. –

+0

durch Hinzufügen von -javaagent Parameter beim Ausführen von Testfall: -javaagent: JaCoCoProject/lib/jacocoagent.jar = destfile = JaCoCoProject/jacoco.exec, output = Datei – anon

+1

Bitte teilen Sie die vollständige Build-Datei. Alles was der Fehler sagt, ist, dass Sie ein jar instrumentiert haben, Tests ausgeführt haben. Diese Klassen stammen nicht von "./jar/abc.jar". Es ist nicht genug, die Klassen nach Namen zu sortieren. Sie müssen der "classID" von jacoco entsprechen. Das bedeutet, dass die Klassen auch dann unterschiedlich sind, wenn Sie sie neu kompilieren. – Jayan

Antwort

4

JaCoCo benötigt exakt die gleichen Klassendateien für die Berichterstellung, die zur Ausführungszeit verwendet wurde. Aufgrund unterschiedlicher Compiler und/oder anderer Tools, die die Klassen ändern, können die Klassen unterschiedlich sein.

13

Sie erhalten den Fehler in Bezug auf ClassID. Dies ist ein Konzept, das auf der Docs-Site von JaCoCo detailliert beschrieben wird. http://www.eclemma.org/jacoco/trunk/doc/classids.html. Dies ist ein wichtiger Schritt zur Unterstützung mehrerer Klassenversionen (z. B. eines Anwendungsservers) in derselben JVM.

Kopieren Teil ein Teil davon hier für Sichtbarkeit.

Was sind Klassen-IDs und wie werden sie erstellt?

Klassen-IDs sind 64-Bit-Integer-Werte, zum Beispiel in 0x638e104737889183 hex Notation. Ihre Berechnung gilt als Implementierungsdetail von JaCoCo. Derzeit werden IDs mit einer CRC64-Prüfsumme der rohen Klassendatei erstellt.

Was kann verschiedene Klassen-IDs verursachen?

Klassen-IDs identisch sind für die exakt gleiche Klassendatei nur (Byte-für-Byte). Es gibt einige Gründe, warum Sie verschiedene Klassendateien erhalten könnten. Zuerst kompilieren Java-Quelldateien werden in verschiedenen Klassendateien führen, wenn Sie eine andere Werkzeugkette verwenden:

  • Verschiedene Compiler-Anbieter (zB Eclipse vs. Oracle JDK)

  • Verschiedene Compiler-Versionen

  • Verschiedene Compiler-Einstellungen (zB Debug vs. nicht-debug)

Auch Post-Processing-Klassendateien (Verschleierung, AspectJ, etc.) werden in der Regel die Klassendateien ändern. JaCoCo funktioniert gut, wenn Sie zur Laufzeit und zur Analyse einfach die gleichen Klassendateien verwenden. Daher spielt die Werkzeugkette zum Erstellen dieser Klassendateien keine Rolle.

Auch wenn die Klassendateien im Dateisystem identisch sind, ist es möglich, dass Klassen, die vom JaCoCo-Laufzeit-Agent gesehen werden, sich sowieso unterscheiden. Dies geschieht normalerweise, wenn ein anderer Java-Agent konfiguriert wird, bevor der JaCoCo-Agent oder spezielle Klassenladeprogramme die Klassendateien vorverarbeiten.Typische Kandidaten sind:

  • Mocking Frameworks
  • Anwendungsserver
  • Persistenz-Frameworks

Die gleiche Seite deckt mögliche Lösungen.

Welche Problemumgehungen gibt es, um mit Laufzeit-modifizierten Klassen umzugehen?

Wenn Klassen zur Laufzeit in Ihrem Setup geändert bekommen gibt es einige Workarounds JaCoCo sowieso funktioniert:

  • Wenn Sie eine andere Java-Agenten machen verwenden sicher, dass das JaCoCo Mittel zunächst in der Befehlszeile angegeben wird. Auf diese Weise sollte der JaCoCo-Agent die ursprünglichen Klassendateien sehen.
  • Geben Sie die Option classdumpdir des JaCoCo-Agenten an und verwenden Sie die dumped-Klassen bei der Berichterstellung. Beachten Sie, dass nur geladene Klassen ausgegeben werden, d. H. Klassen, die überhaupt nicht ausgeführt werden, erscheinen in Ihrem Bericht nicht als nicht abgedeckt.
  • Verwenden Sie Offline-Instrumentierung, bevor Sie Ihre Tests ausführen. Auf diese Weise werden Klassen von JaCoCo instrumentiert, bevor irgendeine Laufzeitmodifikation stattfinden kann. Beachten Sie, dass in diesem Fall der Bericht mit den ursprünglichen Klassen generiert werden muss, nicht mit den instrumentierten.

Edited on 22-02-2017

Wie Offline-Instrumentation verwenden: Verwendung unter Aufgabe von Daniel Atallah zur Verfügung gestellt.

//Additional SourceSets can be added to the jacocoOfflineSourceSets as needed by 
project.ext.jacocoOfflineSourceSets = [ 'main' ] 
task doJacocoOfflineInstrumentation(dependsOn: [ classes, project.configurations.jacocoAnt ]) { 
    inputs.files classes.outputs.files 
    File outputDir = new File(project.buildDir, 'instrumentedClasses') 
    outputs.dir outputDir 
    doFirst { 
     project.delete(outputDir) 
     ant.taskdef(
      resource: 'org/jacoco/ant/antlib.xml', 
      classpath: project.configurations.jacocoAnt.asPath, 
      uri: 'jacoco' 
     ) 
     def instrumented = false 
     jacocoOfflineSourceSets.each { sourceSetName -> 
      if (file(sourceSets[sourceSetName].output.classesDir).exists()) { 
       def instrumentedClassedDir = "${outputDir}/${sourceSetName}" 
       ant.'jacoco:instrument'(destdir: instrumentedClassedDir) { 
        fileset(dir: sourceSets[sourceSetName].output.classesDir, includes: '**/*.class') 
       } 
       //Replace the classes dir in the test classpath with the instrumented one 
       sourceSets.test.runtimeClasspath -= files(sourceSets[sourceSetName].output.classesDir) 
       sourceSets.test.runtimeClasspath += files(instrumentedClassedDir) 
       instrumented = true 
      } 
     } 
     if (instrumented) { 
      //Disable class verification based on https://github.com/jayway/powermock/issues/375 
      test.jvmArgs += '-noverify' 
     } 
    } 
} 
test.dependsOn doJacocoOfflineInstrumentation 

Jetzt generieren Bericht mit "gradlew test jacocoTestReport" Befehl.

+2

Danke für das Hinzufügen dieser Antwort. Es ist sehr hilfreich beim Verständnis des Jacoco-Frameworks und der damit verbundenen Konzepte. – anon

+0

@Jayan: Deine Antwort ist richtig, aber sehr schwer zu verstehen, was genau ich tun muss, um das Problem zu beheben. Wenn Sie ein Code-Snippet bereitstellen können, wird es für mich sehr viel einfacher.Sie haben die Antwort geschrieben, wenn man bedenkt, dass die Person, die das liest, ein gutes Verständnis davon hat, wie Jacoco arbeitet. –

+1

@Jayan: Bearbeitete Ihre Antwort und fügte eine Aufgabe hinzu, die ich für die Offline-Instrumentierung verwendete. Lesen Sie bitte. –