Android 上的 JaCoCo 仪器化测试失败并显示 'agent not started'
JaCoCo on Android Instrumented Tests fails with 'agent not started'
我们有一个包含一些应用程序和库的多模块项目,并使用 JaCoCo 覆盖 android 上的单元测试和仪器测试。因此,我们将 gradle 的标准 jacoco 插件与 gradle 的 android 插件一起使用。我们使用的 JaCoCo 目标是 createStandardDebugCoverageReport
。
但是,我们看到了一些我们既无法解释也无法更改的行为:
在两个模块上,测试 运行 并通过,但在一个模块上,覆盖率报告是从模拟器创建和下载的。在另一个模块上,我们看到测试通过后的错误信息:
Caused by: java.lang.IllegalStateException: JaCoCo agent not started.
在 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'
//
}
}
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'
//
}
}
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。
您有什么建议、建议或提示吗?
提前致谢。
从插件 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 文件中来统一测试配置,因此关于测试的模块 gradle 配置之间应该没有区别。
这没有帮助,但改进了我的架构。
如果我向存在这些问题的模块添加插桩测试,它就可以正常工作,并且会为所有模块生成覆盖率报告。
我不明白的是,所有这些模块都有一个 ExampleInstrumentedTest
通过。
但是,两个模块确实只有 ExampleInstrumentedTest
并且它会生成覆盖率报告。它们都是 android 个应用程序。但是,我也遇到了无法使用另一个(非常简单的)应用程序生成覆盖率报告的问题。
这就是为什么我一开始没有尝试向失败的模块添加其他插桩测试的原因。
所以,它现在对我有用了。
但是,如果有人能给出提示,为什么它有效而使用 ExampleInstrumentedTest
无效,我们将不胜感激。
我们有一个包含一些应用程序和库的多模块项目,并使用 JaCoCo 覆盖 android 上的单元测试和仪器测试。因此,我们将 gradle 的标准 jacoco 插件与 gradle 的 android 插件一起使用。我们使用的 JaCoCo 目标是 createStandardDebugCoverageReport
。
但是,我们看到了一些我们既无法解释也无法更改的行为: 在两个模块上,测试 运行 并通过,但在一个模块上,覆盖率报告是从模拟器创建和下载的。在另一个模块上,我们看到测试通过后的错误信息:
Caused by: java.lang.IllegalStateException: JaCoCo agent not started.
在 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'
//
}
}
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'
//
}
}
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。 您有什么建议、建议或提示吗?
提前致谢。
从插件 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 文件中来统一测试配置,因此关于测试的模块 gradle 配置之间应该没有区别。
这没有帮助,但改进了我的架构。
如果我向存在这些问题的模块添加插桩测试,它就可以正常工作,并且会为所有模块生成覆盖率报告。
我不明白的是,所有这些模块都有一个 ExampleInstrumentedTest
通过。
但是,两个模块确实只有 ExampleInstrumentedTest
并且它会生成覆盖率报告。它们都是 android 个应用程序。但是,我也遇到了无法使用另一个(非常简单的)应用程序生成覆盖率报告的问题。
这就是为什么我一开始没有尝试向失败的模块添加其他插桩测试的原因。
所以,它现在对我有用了。
但是,如果有人能给出提示,为什么它有效而使用 ExampleInstrumentedTest
无效,我们将不胜感激。