使用Gradle运行所有子项目测试

时间:2019-06-22 00:34:00

标签: android gradle kotlin android-gradle

我有一个结构如下的多模块android项目:

:a
:b
  :c
  :d
    :e

我正在尝试在模块:b上运行jacoco报告,以使其在:b,:c,:d和:e上运行而不运行:a。我希望所有xml报告都位于一个公用文件夹中,并带有其project.xml的名称(例如b.xml,c.xml等)。我有一个非常标准的jacoco设置

task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) {

    reports {
        xml.enabled = true
        xml.destination = file(allTestCoverageDir + project.name + ".xml")
        html.enabled = true
    }

    def fileFilter = [
            '**/R.class',
            '**/R$*.class',
            '**/BuildConfig.*',
            '**/Manifest*.*',
            '**/*Test*.*',
            'android/**/*.*',

            //Dagger 2
            '**/*Dagger*Component*.*',
            '**/*Module.*',
            '**/*Module$*.*',
            '**/*MembersInjector*.*',
            '**/*_Factory*.*',
            '**/*Provide*Factory*.*',
    ]

    def kotlinDebug = [fileTree(dir: "${project.buildDir}/tmp/kotlin-classes/debug", excludes: fileFilter)]
    def mainSrc = files([
            "$project.projectDir/src/main/java",
            "$project.projectDir/src/main/kotlin"
    ])

    sourceDirectories = files([mainSrc])
    classDirectories = files(kotlinDebug)
    executionData = fileTree(dir: project.buildDir, includes: [
            'jacoco/testDebugUnitTest.exec', 'outputs/code-coverage/connected/*coverage.ec'
    ])
}

但是,当我尝试遍历doLast块中的子项目时,doLast块永远不会运行,并且尝试访问子项目之前,它还表明:a没有子项目。

编辑我可以使用./gradlew b:jacocoTestReport或./gradlew c:jacocoTestReport以及所有报告并在具有正确名称的文件夹中为每个子项目运行这些文件。但是随着项目的增长,我不想运行数十个命令(每个模块一个),我只想运行一个命令./gradlew b:jacocoTestReport(或类似的命令),它可以为b及其子树运行

1 个答案:

答案 0 :(得分:0)

据我目前的了解,您正在使用allprojects {}来配置所有子项目。尽管过去这一直是规范一组项目的规范,但现在不建议这样做。此外,项目应使用出版物相互连接,而不是跨项目边界复制文件。因此,您需要做两件事:

  1. 您应该创建一个插件来配置jacoco并创建用于保存报告的配置,而不是从根root项目配置子项目。

为此,请在您的项目中创建一个pre-compiled script plugin。想法是在buildSrc项目中使用kotlin构建脚本,并从该文件动态创建Gradle插件。因此,您应该将配置jacoco的逻辑移至文件buildSrc/src/main/kotlin/jacoco-conventions.gradle.kts

plugins {
    jacoco
}

val jacocoTestReport by tasks.getting(JacocoReport::class) {
  // jacoco configuration 
}

configurations.create("jacocoReports") {
    isCanBeResolved = false
    isCanBeConsumed = true
    attributes {
        attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class, "jacocoReports"))
    }
    outgoing.artifact(jacocoTestReport.reports.xml.destination) {
        builtBy(jacocoTestReport)
    }
}

最后一部分在应用预编译脚本插件的项目中创建新配置。此配置使用由jacoco report任务构建的xml目标文件作为传出工件。这里的重要部分是USAGE_ATTRIBUTE,因为稍后我们将需要它来使用文件。

现在可以通过以下方式将预编译的脚本插件应用于想要收集jacoco指标的项目中:

// for example in c/build.gradle.kts
plugins {
  `jacoco-conventions`
}

现在,您已经配置了子项目,以将Jacoco xml报告放入使用属性为jacocoReports的配置中。

  1. 在根项目中,创建一个任务,该任务将从配置中复制报告。

为此,我们需要设置一个使用jacocoReports变体的配置,然后依赖于子项目的变体:

// main build file
val jacocoReports by configurations.creating {
    isCanBeResolved = true
    isCanBeConsumed = false
    attributes {
        attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class, "jacocoReports"))
    }
}

dependencies {
    jacocoReports(project(":b:c"))
    jacocoReports(project(":b:d:e"))
    // other jacocoReports you want to consume
}

tasks.register<Copy>("aggregateJacocoReports") {
    from(jacocoReports)
    into(file("$buildDir/jacoco"))
}

如您所见,jacocoReports配置具有相同的用法属性,因此可用于解析具有相同属性的配置中的文件。然后,我们需要定义我们要使用的项目报告。通过使用jacocoReports配置定义项目依赖关系来达到锥度。最后一步是一个简单的复制任务,它将文件复制到根项目的构建目录中。因此,现在,当您调用./gradlew aggregateJacocoReports时,此任务将解析​​jacocoReports配置中的所有文件,这将反过来为根项目所依赖的所有项目创建jacoco报告。

为什么这比交叉配置更好?如果项目没有被交叉配置和在项目之间复制内容的任务所困扰,那么gradle可以更有效地安排和并行化必须完成的工作。

我创建了一个最小的示例,该示例可以帮助您以这种方式设置项目:https://github.com/britter/gradle-jacoco-aggregate。为了简化起见,我删除了android特定的配置,但是我敢肯定您会弄清楚的。