2013-08-21 4 views
32

Ich bin neu bei Gradle und Android testen, aber ich habe bereits mein Android-Projekt konvertiert, um mit Gradle zu bauen.Android Testcode Abdeckung mit JaCoCo Gradle Plugin

Jetzt versuche ich die Testabdeckung eines Android-Projekts mit Gradles JaCoCo-Plugin durchzuführen.

Ich habe folgend meine build.gradle Datei hinzugefügt:

apply plugin: 'jacoco' 

Und wenn ich "gradle jacocoTestReport" die folgenden Fehler führen:

Task 'jacocoTestReport' not found in root project '<project name>'. 

Aus der Dokumentation sollte mich um auch das Plugin 'java' anzuwenden, aber es steht im Widerspruch zum Plugin 'android'.

Gibt es einen Weg um dies?

Vielen Dank im Voraus.

+0

Testabdeckung wird vom android Gradle Plugin noch nicht unterstützt. Ich bin auf der Suche nach einem Weg, um zu erreichen, aber es sieht hoffnungslos jetzt aus, wie die Gradle-Plugin für Android Android nicht sagen, um jede Berichterstattung zu generieren. – Snicolas

+0

bis das Java-Plugin und das Android-Plugin miteinander kompatibel sind, können Sie ant.java verwenden, um die Tests auszuführen und auch den Abdeckungsbericht zu erstellen. tun Sie im Grunde, was Sie in ANT tun würden. – skipy

+2

@skipy: Hast du ein Beispiel, wie man das in Ameisen macht? Ich konnte kein Beispiel für die Konfiguration von 'jacocoagent' finden und den Bericht vom Emulator abrufen. – unholysampler

Antwort

22

Hier ist, wie ich bin mit Jacoco:

buildscript { 
    repositories { 
    mavenLocal() 
    mavenCentral() 
    } 
    dependencies { 
    classpath 'com.android.tools.build:gradle:0.12.+' 
    classpath 'org.robolectric:robolectric-gradle-plugin:0.11.+' 
    } 
} 

apply plugin: 'com.android.application' 
apply plugin: 'robolectric' 
apply plugin: 'jacoco' 

android { 
    compileSdkVersion 20 
    buildToolsVersion "20.0.0" 

    defaultConfig { 
    applicationId "YOUR_PACKAGE_NAME" 
    minSdkVersion 10 
    targetSdkVersion 20 
    testHandleProfiling true 
    testFunctionalTest true 
    } 
    buildTypes { 
    debug { 
     testCoverageEnabled false 
    } 
    release { 
     runProguard false 
     proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 
    } 
    } 
    jacoco { 
    version "0.7.1.201405082137" 
    } 
    packagingOptions { 
    exclude 'META-INF/DEPENDENCIES.txt' 
    exclude 'META-INF/LICENSE.txt' 
    exclude 'META-INF/NOTICE.txt' 
    exclude 'META-INF/NOTICE' 
    exclude 'META-INF/LICENSE' 
    exclude 'META-INF/DEPENDENCIES' 
    exclude 'META-INF/notice.txt' 
    exclude 'META-INF/license.txt' 
    exclude 'META-INF/dependencies.txt' 
    exclude 'META-INF/LGPL2.1' 
    exclude 'META-INF/services/javax.annotation.processing.Processor' 
    exclude 'LICENSE.txt' 
    } 
} 

robolectric { 
    include '**/*Test.class' 
    exclude '**/espresso/**/*.class' 

    maxHeapSize "2048m" 
} 

jacoco { 
    toolVersion "0.7.1.201405082137" 
} 

// Define coverage source. 
// If you have rs/aidl etc... add them here. 
def coverageSourceDirs = [ 
    'src/main/java', 
] 

task jacocoTestReport(type: JacocoReport, dependsOn: "connectedDebugAndroidTest") { 
    group = "Reporting" 
    description = "Generate Jacoco coverage reports after running tests." 
    reports { 
    xml.enabled = true 
    html.enabled = true 
    } 
    classDirectories = fileTree(
     dir: './build/intermediates/classes/debug', 
     excludes: ['**/R*.class', 
       '**/*$InjectAdapter.class', 
       '**/*$ModuleAdapter.class', 
       '**/*$ViewInjector*.class' 
     ]) 
    sourceDirectories = files(coverageSourceDirs) 
    executionData = files("$buildDir/jacoco/testDebug.exec") 
    // Bit hacky but fixes https://code.google.com/p/android/issues/detail?id=69174. 
    // We iterate through the compiled .class tree and rename $$ to $. 
    doFirst { 
    new File("$buildDir/intermediates/classes/").eachFileRecurse { file -> 
     if (file.name.contains('$$')) { 
     file.renameTo(file.path.replace('$$', '$')) 
     } 
    } 
    } 
} 


dependencies { 
    androidTestCompile('junit:junit:4.11') { 
    exclude module: 'hamcrest-core' 
    } 
    androidTestCompile('org.robolectric:robolectric:2.3') { 
    exclude module: 'classworlds' 
    exclude module: 'maven-artifact' 
    exclude module: 'maven-artifact-manager' 
    exclude module: 'maven-error-diagnostics' 
    exclude module: 'maven-model' 
    exclude module: 'maven-plugin-registry' 
    exclude module: 'maven-profile' 
    exclude module: 'maven-project' 
    exclude module: 'maven-settings' 
    exclude module: 'nekohtml' 
    exclude module: 'plexus-container-default' 
    exclude module: 'plexus-interpolation' 
    exclude module: 'plexus-utils' 
    exclude module: 'wagon-file' 
    exclude module: 'wagon-http-lightweight' 
    exclude module: 'wagon-http-shared' 
    exclude module: 'wagon-provider-api' 
    exclude group: 'com.android.support', module: 'support-v4' 
    } 
} 

Der obige Code enthält auch eine Lösung für https://code.google.com/p/android/issues/detail?id=69174.

Weitere Details: http://chrisjenx.com/gradle-robolectric-jacoco-dagger/

+0

Wie hast du das obige Skript mit dem Android-Plugin integriert? Können Sie mir den Link geben, um Ihre Dateien zu sehen? – Borys

+0

@Borys nehmen an, Sie haben bereits eine Existenz 'build.gradle' mit robolectric integriert. Sie müssen nur 'apply plugin: 'jacoco'' im 'apply plugin'-Bereich setzen, dann den Rest des obigen Codes an das Ende Ihres' build.gradle' setzen. Sie können dann './gradlew testDebug jacocoTestReport' ausführen. Das ist es. –

+0

Hm .... Ich habe folgenden Fehler: "Konnte die Abhängigkeiten der Aufgabe nicht ermitteln: app: jacocoTestReport '" Was ich vermisse? – Borys

-3

Haben Sie versucht, Folgendes hinzufügen:

jacocoTestReport { 
    group = "Reporting" 
    description = "Generate Jacoco coverage reports after running tests." 
    additionalSourceDirs = files(sourceSets.main.allJava.srcDirs) 
} 

Und dann statt laufen ./gradlew jacocoTestReport laufen ./gradlew test jacocoTestReport.

Wenn alles gut geht, sollten Sie die Testergebnisse bei build/reports/jacoco/test/html/index.html finden.

+1

Versuchte Ihre Lösung, hier ist, was ich bekomme: > Konnte Methode jacocoTestReport() für Argumente [Build_5dkpq0odkgno9tsiihqqr1k86u $ _run_closure4 @ 49c96202] auf Projekt ': App' nicht finden. – Egor

+1

jacoco plugin ist nicht kompatibel mit android plugin. jacoco plugin benötigt Java-Projekt. – skyisle

+0

Also, wie man es in Android-Projekt ausführen? – Borys

6

Ich verwende JaCoCo mit einem Projekt mit RoboGuice, Buttermesser und Robolectric. Ich konnte es mit der Lösung von @Hieu Rocker einrichten, jedoch gab es einige kleinere Nachteile, d. H. In unserem Projekt verwenden wir Aromen und haben einige zusätzliche Tests für diese Aromen sowie zusätzlichen Java-Code für jede von ihnen. Wir verwenden auch verschiedene Build-Typen. Daher war eine Lösung, die auf die Aufgabe "testDebug" angewiesen war, nicht gut genug. Hier ist meine Lösung: In build.gradle in App Modul

hinzufügen
apply from: '../app/jacoco.gradle' 

dann innerhalb von App Modul mit folgendem Inhalt eine jacoco.gradle-Datei erstellen:

 

    apply plugin: 'jacoco' 

    jacoco { 
     toolVersion "0.7.1.201405082137" 
    } 

    def getFlavorFromVariant(String variantName) { 
     def flavorString = variantName.replaceAll(/(.*)([A-Z].*)/) { all, flavorName, buildTypeName -> 
      flavorName 
     } 
     return flavorString; 
    } 

    def getBuildTypeFromVariant(String variantName) { 
     def buildTypeString = variantName.replaceAll(/(.*)([A-Z].*)/) { all, flavorName, buildTypeName -> 
      "${buildTypeName.toLowerCase()}" 
     } 
     return buildTypeString; 
    } 

    def getFullTestTaskName(String variantName) { 
     return "test${variantName.capitalize()}UnitTest"; 
    } 

    android.applicationVariants.all { variant -> 
     def variantName = variant.name; 
     def flavorFromVariant = getFlavorFromVariant("${variantName}"); 
     def buildTypeFromVariant = getBuildTypeFromVariant("${variantName}"); 
     def testTaskName = getFullTestTaskName("${variantName}") 

     task ("jacoco${variantName.capitalize()}TestReport", type: JacocoReport, dependsOn: testTaskName) { 
      group = "Reporting" 
      description = "Generate JaCoCo coverage reports after running tests for variant: ${variantName}." 
      reports { 
       xml.enabled = true 
       html.enabled = true 
      } 

      classDirectories = fileTree(
        dir: "./build/intermediates/classes/${flavorFromVariant}/${buildTypeFromVariant}", 
        excludes: ['**/R*.class', 
           '**/*$InjectAdapter.class', 
           '**/*$ModuleAdapter.class', 
           '**/*$ViewInjector*.class' 
        ] 
      ) 

      logger.info("Configuring JaCoCo for flavor: ${flavorFromVariant}, buildType: ${buildTypeFromVariant}, task: ${testTaskName}"); 

      def coverageSourceDirs = [ 
        '../app/src/main/java', 
        "../app/src/${flavorFromVariant}/java" 
      ] 
      sourceDirectories = files(coverageSourceDirs) 
      executionData = files("$buildDir/jacoco/${testTaskName}.exec") 
      // Bit hacky but fixes https://code.google.com/p/android/issues/detail?id=69174. 
      // We iterate through the compiled .class tree and rename $$ to $. 
      doFirst { 
       new File("$buildDir/intermediates/classes/").eachFileRecurse { file -> 
        if (file.name.contains('$$')) { 
         file.renameTo(file.path.replace('$$', '$')) 
        } 
       } 
      } 
     } 
    } 

Sie können es über die Befehlszeile wie folgt ausführen:

.gradlew jacocoFlavor1DebugTestReport 

oder

.gradlew jacocoOtherflavorPrereleaseTestReport 

In unserem Projekt verwenden wir eine Konvention nicht Großbuchstaben innerhalb des Aromas zu verwenden und Typnamen zu bauen, aber wenn Ihr Projekt können Sie einfach getFlavorFromVariant ändert sich nicht an dieser Konvention (. .) und getBuildTypeFromVariant (..) Funktionen

Hoffnung hilft dieses jemand

+4

Hallo Piotr, Im bekommen Konnte die Abhängigkeiten von Task 'nicht bestimmen: app: jacocoDebugTestReport'. > Aufgabe mit Pfad 'testDebug' nicht in Projekt ': app' gefunden. irgendeine Idee warum? – AndroidGecko

+0

@AndroidGecko Ich habe die Antwort aktualisiert. Der Name der Testtask wird nun mit 'UnitTest' am Ende angehängt. Ich hoffe es hilft! –