简体   繁体   English

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

[英]Run all subprojects tests using Gradle

I have a multi-module android project with the structure: 我有一个多模块的android项目,结构如下:

:a
:b
  :c
  :d
    :e

I'm trying to run a jacoco report on module :b so that it runs on :b, :c, :d, and :e without running :a. 我正在尝试在模块上运行jacoco报告:b以便它运行在:b,:c,:d和:e没有运行:a。 I want all of the xml reports to be in a common folder with names of their project.xml (egbxml, c.xml, etc.) I have a pretty standard jacoco setup 我希望所有的xml报告都在一个公共文件夹中,其中包含project.xml的名称(egbxml,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'
    ])
}

But when I try to loop through subprojects in a doLast block, the doLast block never runs and trying to access subprojects before that also shows that :a has no subprojects. 但是当我尝试遍历doLast块中的子项目时,doLast块从不运行并尝试访问子项目,之后还显示:a没有子项目。

Edit I am able to run these for each sub project with ./gradlew b:jacocoTestReport or ./gradlew c:jacocoTestReport and all the reports and in a folder with the correct names. 编辑我可以使用./gradlew b:jacocoTestReport或./gradlew c:jacocoTestReport和所有报告以及具有正确名称的文件夹为每个子项目运行这些项目。 But as my project grows I don't want to have to run dozens of commands (one for each module) I want a single command ./gradlew b:jacocoTestReport (or something similar) which runs for b and it's subtree 但随着我的项目的增长,我不想运行几十个命令(每个模块一个)我想要一个命令./gradlew b:jacocoTestReport(或类似的东西)运行b和它的子树

As far as I understand at the moment you're using allprojects {} to configure all of the sub projects. 据我所知,您现在正在使用allprojects {}来配置所有子项目。 Although this has been the canonical to configure a group of projects in the past, it is now discouraged. 虽然这是过去配置一组项目的规范,但现在不鼓励这样做。 Furthermore projects should use publications to interface with each other instead of copying files across project boundaries. 此外,项目应使用出版物相互连接,而不是跨项目边界复制文件。 So you need to do two things: 所以你需要做两件事:

  1. Instead of configuring the sub projects from the root root project, you should create a plugin to configure jacoco and create configuration that will hold the reports. 您应该创建一个插件来配置jacoco并创建将保存报告的配置,而不是从根目录项目配置子项目。

To do this, create a pre-compiled script plugin in your project. 为此,请在项目中创建预编译的脚本插件 The idea is to have a kotlin build script in the buildSrc project and create a Gradle plugin from that file on the fly. 我们的想法是在buildSrc项目中创建一个kotlin构建脚本,并在buildSrc从该文件创建一个Gradle插件。 So you should move the logic that configures jacoco to the file buildSrc/src/main/kotlin/jacoco-conventions.gradle.kts : 因此,您应该将配置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)
    }
}

The last part creates a new configuration in the project the pre-compiled script plugin is applied to. 最后一部分在应用预编译脚本插件的项目中创建新配置。 This configuration uses the xml destination file which is builtBy the jacoco report task as an outgoing artifact. 此配置使用xml目标文件,该文件由jacoco报告任务构建为传出工件。 The important part here is the USAGE_ATTRIBUTE because we will need this later on to consume the files. 这里的重要部分是USAGE_ATTRIBUTE因为我们稍后需要使用它来使用文件。

The precompiled script plugin can now be applied in the projects where you want to gather jacoco metrics by: 预编译的脚本插件现在可以应用于您希望通过以下方式收集jacoco指标的项目中:

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

Now you have configured the sub projects to put the Jacoco xml reports into configurations with usage attribute jacocoReports . 现在,您已配置子项目以将Jacoco xml报告放入具有使用属性jacocoReports配置中。

  1. In the root project create a task that copies the report from the configuration. 在根项目中,创建一个从配置中复制报告的任务。

To do this we need to setup a configuration that consumes the jacocoReports variants and then depend on the variants of the sub projects: 为此,我们需要设置一个使用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"))
}

As you can see, the jacocoReports configuration has the same usage attribute, so it can be used to resolve files that are in configurations with the same attribute. 如您所见, jacocoReports配置具有相同的用法属性,因此可用于解析具有相同属性的配置中的文件。 Then we need to define which project reports we want to consume. 然后我们需要定义我们想要使用的项目报告。 This is cone by defining project dependencies using the jacocoReports configuration. 这是通过使用jacocoReports配置定义项目依赖关系来实现的。 The last step is a simple copy task that copies the files into the build directory of the root project. 最后一步是一个简单的复制任务,它将文件复制到根项目的构建目录中。 So now when you call ./gradlew aggregateJacocoReports this task resolves all the files from the jacocoReports configuration, which in turn will create the jacoco report for all projects that are the root project has a dependency on. 所以现在当你调用./gradlew aggregateJacocoReports这个任务解析了jacocoReports配置中的所有文件,这反过来将为根项目依赖的所有项目创建jacoco报告。

Why is this better than cross configuration? 为什么这比交叉配置更好? If projects are not entangled by cross configuration and by tasks that copy stuff between projects, gradle can more efficiently schedule and parallelize the work that has to be done. 如果项目不是由交叉配置和在项目之间复制内容的任务纠缠在一起,那么gradle可以更有效地安排和并行化必须完成的工作。

I have a created a minimal example that should help you to setup your project this way: https://github.com/britter/gradle-jacoco-aggregate . 我创建了一个最小的示例,可以帮助您以这种方式设置项目: https//github.com/britter/gradle-jacoco-aggregate I have removed the android specific configuration to keep it simple, but I'm sure you will figure it out. 我已经删除了android特定的配置以保持简单,但我相信你会弄明白的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM