【问题标题】:JaCoCo on Android Instrumented Tests fails with 'agent not started'Android Instrumented Tests 上的 JaCoCo 因“代理未启动”而失败
【发布时间】:2018-11-02 09:17:49
【问题描述】:

我们有一个包含一些应用程序和库的多模块项目,并使用 JaCoCo 覆盖 android 上的单元测试和仪器测试。因此,我们将标准的 jacoco gradle 插件与 android 插件一起用于 gradle。我们使用的 JaCoCo 目标是createStandardDebugCoverageReport。

但是,我们看到了一些既无法解释也无法改变的行为: 在这两个模块上,测试都运行并通过,但在一个模块上,覆盖率报告是从模拟器中创建和下载的。在另一个模块上,我们看到测试通过后的错误信息:

原因:java.lang.IllegalStateException: JaCoCo 代理未启动。

在 logcat 上,因此会创建一个零字节的文件。

我们使用两个类似的应用程序对其进行了测试,这些应用程序具有 ExampleUnitTest 和 ExampleInstrumentedTest 类。

整个 JaCoCo 配置在项目的根 gradle 中完成。

项目的根 Gradle:

import org.ajoberstar.grgit.Grgit

buildscript {
    ext {
        jacocoVersion = "0.8.1"
    }
    repositories {
        maven {
            url "our.artifactory.url"
            credentials {
                username System.getenv("ARTIFACTORY_LOCAL_USERNAME") ? System.getenv("ARTIFACTORY_LOCAL_USERNAME") : project.property("ARTIFACTORY_LOCAL_USERNAME")
                password System.getenv("ARTIFACTORY_LOCAL_PASSWORD") ? System.getenv("ARTIFACTORY_LOCAL_PASSWORD") : project.property("ARTIFACTORY_LOCAL_PASSWORD")
            }
        }
        // The used repositories need to be configured within artifactory because we use it as dependency cache.
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.3.0-alpha13'
        classpath 'com.google.gms:google-services:3.2.0'
        classpath 'io.fabric.tools:gradle:1.26.1'
        classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:4.7.5'
        classpath 'org.ajoberstar:grgit:2.2.1'
        classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.6.2"

        classpath "org.jacoco:org.jacoco.core:${jacocoVersion}"
        classpath "org.jacoco:org.jacoco.report:${jacocoVersion}"
        classpath "org.jacoco:org.jacoco.agent:${jacocoVersion}"

        classpath "org.codehaus.groovy:groovy-all:2.4.15"
    }
}

plugins {
    id "org.sonarqube" version "2.6.2"
}

// This block encapsulates custom properties and makes them available to all
// modules in the project.
ext {
    versionCode = 110
    versionName = "0.17.3"

    currentBranchName = System.getenv("BRANCH")

    def gitDirectory = file(file('.').parentFile.absolutePath + File.separator + '.git')
    if (currentBranchName.equals("unknown") && gitDirectory.exists()) {
        git = Grgit.open(dir: gitDirectory.parentFile)
        currentBranchName = git.branch.current.name
    }

    artifactoryUser = System.getenv("ARTIFACTORY_LOCAL_USERNAME")
    artifactoryPassword = System.getenv("ARTIFACTORY_LOCAL_PASSWORD")

    minSdkVersion = 23
    compileSdkVersion = 28
    targetSdkVersion = 28
    supportLibVersion = "28.0.0"
    playServicesVersion = "16.2.0"
    jacocoVersion = "0.8.1"
}

allprojects {
    apply plugin: "com.jfrog.artifactory"
    apply plugin: 'maven-publish'

    repositories {
        maven {
            url "our.artifactory.url"
            credentials {
                username System.getenv("ARTIFACTORY_LOCAL_USERNAME") ? System.getenv("ARTIFACTORY_LOCAL_USERNAME") : project.property("ARTIFACTORY_LOCAL_USERNAME")
                password System.getenv("ARTIFACTORY_LOCAL_PASSWORD") ? System.getenv("ARTIFACTORY_LOCAL_PASSWORD") : project.property("ARTIFACTORY_LOCAL_PASSWORD")
            }
        }
        // The used repositories need to be configured within artifactory because we use it as dependency cache.
    }

// This is the only place of any JaCoCo configuration
    apply plugin: 'jacoco'

    tasks.withType(Test) {
        jacoco.includeNoLocationClasses = true
    }
}

以下是一个磨损应用程序,工作示例:

apply plugin: 'com.android.application'

configurations {
    demoDebugCompile
    demoReleaseCompile
    standardDebugCompile
    standardReleaseCompile
}

android {
    signingConfigs {
        ourSigningConfig {
            keyAlias getEnvProperty("SIGNING_KEY_ALIAS")
            keyPassword getEnvProperty("SIGNING_KEY_PASSWORD")
            storeFile file(getEnvProperty("SIGNING_STORE_FILE"))
            storePassword getEnvProperty("SIGNING_STORE_PASSWORD")
        }
    }

    compileSdkVersion rootProject.ext.compileSdkVersion

    defaultConfig {
        applicationId "our.working.applicationid"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
        multiDexEnabled true
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            debuggable false
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.ourSigningConfig
        }

        debug {
            debuggable true
            minifyEnabled false
            applicationIdSuffix ".debug"
            versionNameSuffix " Debug"
            testCoverageEnabled true
        }
    }

    flavorDimensions "mode"

    productFlavors {
        demo {
            dimension "mode"
            applicationIdSuffix ".demo"
            versionNameSuffix " Demo"
            buildConfigField "boolean", "DEMO", "true"
        }

        standard {
            dimension "mode"
            buildConfigField "boolean", "DEMO", "false"
        }
    }

    testOptions {
        unitTests {
            includeAndroidResources = true
            returnDefaultValues = false
        }

        unitTests.all {
            jvmArgs '-noverify'
            // https://stackoverflow.com/questions/32315978/jvm-options-in-android-when-run-gradlew-test/37593189#37593189
        }
    }

    lintOptions {
        abortOnError false
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    // External libraries
    implementation fileTree(include: ['*.jar'], dir: 'libs')

    // Support library
    implementation 'com.google.android.support:wearable:2.1.0'
    compileOnly 'com.google.android.wearable:wearable:2.1.0'

    // Google Play services
    implementation "com.google.android.gms:play-services-wearable:15.0.1" // TODO: use ${rootProject.ext.supportLibVersion}

    // Authenticator
    implementation project(':authenticator')

    // Testing
    testImplementation 'junit:junit:4.12'
    testImplementation 'org.robolectric:robolectric:4.0-beta-1'

    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        def requested = details.requested
        if (requested.group == 'com.android.support') {
            if (!requested.name.startsWith("multidex")) {
                details.useVersion "${rootProject.ext.supportLibVersion}"
            }
        }
    }
}

artifactoryPublish.skip = true

以下是一个虚拟应用程序。这个给了我们agent not started exception。 我们比较了模块 gradle 文件,并用工作 gradle 文件的依赖项增强了这个文件。

apply plugin: 'com.android.application'

configurations {
    demoDebugCompile
    demoReleaseCompile
    standardDebugCompile
    standardReleaseCompile
}

android {
    compileSdkVersion rootProject.ext.compileSdkVersion

    defaultConfig {
        applicationId "our.nonworking.applicationid"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName
        multiDexEnabled true
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }

    buildTypes {
        release {
            debuggable false
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            consumerProguardFiles 'proguard-rules.pro'
        }
        debug {
            debuggable true
            minifyEnabled false
            applicationIdSuffix ".debug"
            versionNameSuffix " Debug"
            testCoverageEnabled true
        }
    }

    flavorDimensions "mode"
    productFlavors {
        demo {
            dimension "mode"
            applicationIdSuffix ".demo"
            versionNameSuffix " Demo"
            buildConfigField "boolean", "DEMO", "true"
        }
        standard {
            dimension "mode"
            buildConfigField "boolean", "DEMO", "false"
        }
    }

    testOptions {
        unitTests {
            includeAndroidResources = true
            returnDefaultValues = false
        }
        unitTests.all {
            jvmArgs '-noverify'
            // https://stackoverflow.com/questions/32315978/jvm-options-in-android-when-run-gradlew-test/37593189#37593189
        }
    }

    lintOptions {
        abortOnError false
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')

    implementation 'com.android.support.constraint:constraint-layout:1.1.3'

    // Support library
    implementation 'com.google.android.support:wearable:2.1.0'
    compileOnly 'com.google.android.wearable:wearable:2.1.0'

    // Google Play services
    implementation "com.google.android.gms:play-services-wearable:15.0.1" // TODO: use ${rootProject.ext.supportLibVersion}

    // Authenticator
    implementation project(':authenticator')

    // Testing
    testImplementation 'junit:junit:4.12'
    testImplementation 'org.robolectric:robolectric:4.0-beta-1'

    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

configurations.all {
    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        def requested = details.requested
        if (requested.group == 'com.android.support') {
            if (!requested.name.startsWith("multidex")) {
                details.useVersion "${rootProject.ext.supportLibVersion}"
            }
        }
    }
}

artifactoryPublish.skip = true

我们现在没有主意了。什么可能对此产生影响?我们在项目中的某些库上看到了相同的行为。 测试的模拟器是 Pixel XL API 28。 您有什么建议、意见或提示吗?

提前致谢。

【问题讨论】:

    标签: android android-gradle-plugin android-emulator jacoco


    【解决方案1】:

    从插件 3.0.0 开始,现在您不能使用 android 块 DSL 配置 Jacoco,而是必须在类路径依赖项中定义 jacoco 版本,以及 Android 插件。 所以添加以下内容:

    buildscript {
      repositories {
        google()
        jcenter()
      }
      dependencies {
        // Android plugin 
        classpath 'com.android.tools.build:gradle:3.0.1'
        //Jacoco version
        classpath 'org.jacoco:org.jacoco.core:0.8.1'
      }
    }
    

    希望对你有帮助

    【讨论】:

    • 您好,谢谢您的回答。不幸的是,正如您在我的项目根 gradle 文件中看到的那样,我已经在类路径中拥有了 android 插件和 jacoco 核心以及其他一些依赖项。
    【解决方案2】:

    我找到了一个解决方案,可惜我不完全理解:

    我通过将测试配置提取到一个额外的 gradle 文件来统一测试配置,因此模块 gradle 配置之间在测试方面应该没有区别。

    这并没有帮助,但改进了我的架构。

    如果我向有这些问题的模块添加检测测试,它就会正常工作,并且会为所有模块生成覆盖率报告。 我不明白的是,所有这些模块都有一个通过的ExampleInstrumentedTest。 但是,两个模块确实只有ExampleInstrumentedTest,它会生成覆盖率报告。它们都是 android 应用程序。但是,我也遇到了无法使用另一个(非常简单的)应用程序生成覆盖率报告的问题。 这就是为什么我一开始没有尝试将其他插桩测试添加到失败的模块中。

    所以,它现在对我有用。 但是,如果有人可以提示为什么它有效并且使用 ExampleInstrumentedTest 无效,将不胜感激。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-17
      • 1970-01-01
      • 2021-12-24
      • 1970-01-01
      • 2012-05-13
      • 1970-01-01
      相关资源
      最近更新 更多