web-dev-qa-db-fra.com

Couverture de code pour Jacoco et Unit Tests avec Android-gradle-plugin> = 1.1

J'ai récemment commencé à intégrer Android-gradle-plugin 1.1.0 dans l'un de mes projets. Le projet utilise robolectric 2.4 pour exécuter des tests unitaires.

C'est un projet multi-modules avec des dépendances très complexes (certains modules dépendent d'autres modules). Quelque chose comme ca:

--> application-module (dependsOn: module1, module2, module-core)
    --> module1 (dependsOn: module-core)
    --> module2 (dependsOn: module-core)
    --> module-core (dependsOn: module3, module4)
        --> module3 (library dependencies)
        --> module4 (library dependencies)

Pour une image plus claire, veuillez voir jacoco-example project.

J'ai essayé d'intégrer JaCoCo pour générer des rapports pour les tests unitaires, mais il me semble qu'il n'exécute que androidTests qui sont essentiellement des tests d'instrumentation.

Après quelques recherches sur Google, je suis tombé sur quelques projets sur GitHub et d'autres articles, mais ils se concentrent principalement sur les versions précédentes de Android-gradle-plugin ou utilisez d'autres plugins tiers comme Android-unit-testpar exemple ici .

Peut-être que j'ai perdu ma capacité de google. Mais quelqu'un peut-il m'orienter dans une direction où je peux trouver des documentations concernant les nouveaux trucs dans Android plugin gradle et comment exécuter la tâche jacoco uniquement pour les tests unitaires?

[~ # ~] mise à jour [~ # ~]

Adopté le script de exemple de nenick :

apply plugin: "jacoco"

configurations {
    jacocoReport
}

task jacocoReport(dependsOn: 'testDebug') << {
    ant {
        taskdef(name:'jacocoreport',
                classname: 'org.jacoco.ant.ReportTask',
                classpath: configurations.jacocoReport.asPath)

        mkdir dir: "${buildDir}/test-coverage-report"
        mkdir dir: "${buildDir}/reports/jacoco/test/"

        jacocoreport {
            executiondata = files("${buildDir}/jacoco/testDebug.exec")

            structure(name: "${rootProject.name}") {
                classfiles {
                    fileset (dir: "${buildDir}/intermediates/classes/debug") {
                        //exclude(name: '**/*_*.class')
                        exclude(name: '**/R.class')
                        exclude(name: '**/R$*.class')
                        exclude(name: '**/BuildConfig.class')
                    }
                }

                sourcefiles {
                    fileset dir: "src/main/Java"
                    fileset dir: "${buildDir}/generated/source/buildConfig/debug"
                    fileset dir: "${buildDir}/generated/source/r/debug"
                }
            }

            xml destfile: "${buildDir}/reports/jacoco/test/jacocoTestReport.xml"
            html destdir: "${buildDir}/test-coverage-report/"
        }
    }
}

dependencies {
    jacocoReport 'org.jacoco:org.jacoco.ant:0.7.2.201409121644'
}

Après cela, le ./gradlew jacocoReport exécute et génère le rapport, mais il affiche une couverture de test de 0 (zéro), ce qui est impossible car au moins la moitié de toutes les classes sont testées.

UPDATE_2

J'ai essayé ceci exemple . Ajout de la tâche suivante à l'un de mes fichiers de construction Gradle:

task jacocoTestReport(type:JacocoReport, dependsOn: "testDebug") {
    group = "Reporting"
    description = "Generate Jacoco coverage reports"

    classDirectories = fileTree(
            dir: "${buildDir}/intermediates/classes/debug",
            excludes: ['**/R.class',
                       '**/R$*.class',
                       '**/*$ViewInjector*.*',
                       '**/BuildConfig.*',
                       '**/Manifest*.*']
    )

    sourceDirectories = files("${buildDir.parent}/src/main/Java")
    additionalSourceDirs = files([
            "${buildDir}/generated/source/buildConfig/debug",
            "${buildDir}/generated/source/r/debug"
    ])
    executionData = files("${buildDir}/jacoco/testDebug.exec")

    reports {
        xml.enabled = true
        html.enabled = true
    }
}

Même problème, les rapports sont générés, mais la couverture du code est toujours nulle.

UPDATE_3

Il semble que la tâche de UPDATE_2 a fonctionné mais uniquement pour le module avec apply plugin: 'com.Android.application' (Les rapports sont générés correctement). Mais pour les modules qui sont Android bibliothèques (apply plugin: 'com.Android.library') les rapports montrent une couverture nulle, bien que les modules contiennent plus de tests que le module d'application.

UPDATE_4

J'ai créé un exemple de projet simple qui illustre mon problème. Actuellement, si vous exécutez ./gradlew jacocoReport le rapport est généré, mais aucune couverture de test n'est affichée pour les projets de module. Voir ceci lien

Note courte: Quand les tests étaient AndroidUnitTests (whiteout JUnit 4 et Robolectric), les rapports JaCoCo montraient la couverture pour tous les modules.

Des idées?

35
Serj Lotutovici

Après les tracas, j'ai décidé de créer un plugin Gradle open source pour cela.

Racine build.gradle

buildscript {
    repositories {
        mavenCentral() // optional if you have this one already
    }
    dependencies {
        classpath 'com.vanniktech:gradle-Android-junit-jacoco-plugin:0.8.0'
    }
}

apply plugin: 'com.vanniktech.Android.junit.jacoco'

Ensuite, exécutez simplement

./gradlew jacocoTestReportDebug

Il exécutera les tests JUnit en mode débogage, puis vous donnera la sortie Jacoco au format xml et html dans le répertoire de construction correspondant.

Il prend également en charge les saveurs. Ayant 2 saveurs rouge et bleu, ces tâches seraient créées

  • jacocoTestReportRedDebug
  • jacocoTestReportBlueDebug
  • jacocoTestReportRedRelease
  • jacocoTestReportBlueRelease
14
Niklas

Après quelques recherches supplémentaires, je suis tombé sur cela projet J'ai dû apporter quelques modifications pour que la solution puisse fonctionner pour mon type de projet, mais maintenant les rapports de couverture des tests sont générés correctement.

J'ai poussé les changements adoptés dans mon exemple github repo au cas où quelqu'un aurait un problème similaire à l'avenir.

8
Serj Lotutovici

J'ai configuré mes tests unitaires pour le grade 1.2 en utilisant ceci article de blog . Ensuite, j'ai rassemblé les informations que j'ai trouvées ici et ailleurs pour ajouter une couverture de code à des modules indépendants au lieu de l'ensemble du projet. Dans mon module de bibliothèque build.gradle fichier, j'ai ajouté ce qui suit:

apply plugin: 'jacoco'

def jacocoExcludes = [
        'com/mylibrary/excludedpackage/**'
]

Android {
    ...
}

Android.libraryVariants.all { variant ->
    task("test${variant.name.capitalize()}WithCoverage", type: JacocoReport, dependsOn: "test${variant.name.capitalize()}") {
        group = 'verification'
        description = "Run unit test for the ${variant.name} build with Jacoco code coverage reports."

        classDirectories = fileTree(
                dir: variant.javaCompile.destinationDir,
                excludes: rootProject.ext.jacocoExcludes.plus(jacocoExcludes)
        )
        sourceDirectories = files(variant.javaCompile.source)
        executionData = files("${buildDir}/jacoco/test${variant.name.capitalize()}.exec")

        reports {
            xml.enabled true
            xml.destination "${buildDir}/reports/jacoco/${variant.name}/${variant.name}.xml"
            html.destination "${buildDir}/reports/jacoco/${variant.name}/html"
        }
    }
}

Et dans mon projet build.gradle fichier, j'ai ajouté des exclusions courantes:

ext.jacocoExcludes = [
    'Android/**',
    '**/*$$*',
    '**/R.class',
    '**/R$*.class',
    '**/BuildConfig.*',
    '**/Manifest*.*',
    '**/*Service.*'
]

En outre, il semble que la couverture de code pour les tests unitaires puisse être intégrée à l'avenir problème 144664

3
Chris Feist

J'ai enfin pu voir ma couverture de code des tests JUnit avec Android Studio 1.1.

jacoco.gradle

apply plugin: 'jacoco'

jacoco {
    toolVersion "0.7.1.201405082137"
}

def coverageSourceDirs = [
        "$projectDir/src/main/Java",
]

task jacocoTestReport(type: JacocoReport, dependsOn: "testDebug") {
    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('$$', '$'))
            }
        }
    }
}

puis dans le fichier build.gradle du module (je le mets entre Android et dependencies):

apply from: '../jacoco.gradle'

Toujours dans le bloc defaultConfig de Android. J'ai ajouté ceci (je ne sais pas si c'est nécessaire, mais je l'ai obtenu de ce blog ):

Android {
    defaultConfig {
        testHandleProfiling true
        testFunctionalTest true
    }
}

Prendre plaisir.

2
Niklas

Attention: c'est un hack! En utilisant votre configuration ci-dessus, j'ai mis en place un hack pour basculer le Android entre l'application et la bibliothèque en fonction des tâches de construction choisies. Cela fonctionne bien pour moi car je ne finis pas par valider du code avec le mode d'application défini.

// dynamically change the Android plugin to application if we are running unit tests or test reports.
project.ext.androidPlugin = 'com.Android.library'
for (String taskName : project.gradle.startParameter.taskNames) {
    if (taskName.contains('UnitTest') || taskName.contains('jacocoTestReport')) {
        project.ext.androidPlugin = 'com.Android.application'
        break
    }
}

logger.lifecycle("Setting Android pluging to ${project.ext.androidPlugin}")
apply plugin: project.ext.androidPlugin

...

apply plugin: 'jacoco'

configurations {
    jacocoReport
}

task jacocoTestReport(type:JacocoReport, dependsOn: "testDebug") {
    group = "Reporting"
    description = "Generate Jacoco coverage reports"

    classDirectories = fileTree(
            dir: "${buildDir}/intermediates/classes/debug",
            excludes: ['**/R.class',
                       '**/R$*.class',
                       '**/*$ViewInjector*.*',
                       '**/BuildConfig.*',
                       '**/Manifest*.*']
    )

    sourceDirectories = files("${buildDir.parent}/src/main/Java")
    additionalSourceDirs = files([
            "${buildDir}/generated/source/buildConfig/debug",
            "${buildDir}/generated/source/r/debug"
    ])
    executionData = files("${buildDir}/jacoco/testDebug.exec")

    reports {
        xml.enabled = true
        html.enabled = true
    }
}

Espérons que l'équipe d'outils Android tools corrige cela bientôt.

2
vangorra

Vous pouvez essayer d'utiliser ce plugin Gradle: https://github.com/arturdm/jacoco-Android-gradle-plugin

Fondamentalement, tout ce que vous devez faire est de l'appliquer comme ceci:

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath 'com.dicedmelon.gradle:jacoco-Android:0.1.1'
  }
}

apply plugin: 'com.Android.library' // or 'com.Android.application'
apply plugin: 'jacoco-Android'

Par conséquent, vous devriez obtenir une tâche JacocoReport pour chaque variante. Exécutez la commande ci-dessous pour générer des rapports de couverture de code pour chacun d'eux.

$ ./gradlew jacocoTestReport
1
Artur Stepniewski

J'étais confronté exactement au même problème que vous. Aujourd'hui, j'ai complètement supprimé Android studio, Android sdk, gradle. Ensuite, réinstallez tout. Après cela, je viens d'ajouter à l'intérieur de l'application build.gradle.

debug {testCoverageEnabled true} Ensuite, je lance ./gradlew connectedChec. Tout fonctionne parfaitement. Android studio par défaut Jacoco fonctionne bien pour moi. Je pense qu'il est également possible de créer une tâche jacocoTestReport puis de créer une couverture de code.Je ne sais pas pourquoi gradle et Android le studio ne fonctionnait pas auparavant.

1
user1365169

Je résous les problèmes avec JaCoCo et le fais fonctionner avec le dernier gradle Android plugin 1.1.3

Projet avec les derniers scripts de gradle: https://github.com/OleksandrKucherenko/meter

Les références:

Comment attacher sa propre implémentation au lieu de Mocks dans Android Tests unitaires studio? https: // plus .google.com/117981280628062796190/posts/8jWV22mnqUB

Petit indice pour tous ceux qui essaient d'utiliser la couverture JaCoCo dans Android builds ... conclusion inattendue !!! - https://plus.google.com/117981280628062796190/posts/RreU44qmeuP

Rapport JaCoCo XML/HTML pour les tests unitaires https://plus.google.com/u/0/+OleksandrKucherenko/posts/6vNWkkLed3b

1

Veuillez créer un exemple et je pourrai y jeter un œil. Je suppose que c'est une configuration de chemin manquante.

  • inclure tous les fichiers de couverture (* .exec)
  • ajoutez tous vos chemins source (module/src/main/Java)
  • ajouter tous les chemins de classe (module/build/intermediates/classes/debug)

voici deux exemples à quoi cela pourrait ressembler

0
nenick