Jacoco覆盖率报告问题

15
我试图定义位置,Jacoco将在实际设备上运行的仪器测试中创建覆盖文件。
从Gradle任务的 --debug 运行中,我看到了这个日志:
[DEBUG] [org.gradle.api.Task] DeviceConnector 'Nexus 5X - 6.0.1': installing /home/martin/workspace/lib/my-lib/build/outputs/apk/my-lib-debug-androidTest-unaligned.apk
[INFO] [org.gradle.api.Task] Starting 1 tests on Nexus 5X - 6.0.1
[INFO] [org.gradle.api.Task]  de.my.lib.utils.UtilsTest testMyTest[Nexus 5X - 6.0.1] [32mSUCCESS [0m
[DEBUG] [org.gradle.api.Task] DeviceConnector 'Nexus 5X - 6.0.1': fetching coverage data from /data/data/de.my.lib.test/coverage.ec
[DEBUG] [org.gradle.api.Task] DeviceConnector 'Nexus 5X - 6.0.1': uninstalling de.my.lib.test 13:46:14.538
[DEBUG] [org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter] Finished executing task ':my-lib:connectedDebugAndroidTest'

我尝试了三种方法来定义位置:

在清单文件中使用<instrumentation>标签没有改变任何内容。

<?xml version="1.0" encoding="utf-8"?>
<manifest
    package="de.my.lib.test"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <instrumentation
        android:name="android.support.test.runner.AndroidJUnitRunner"
        xmlns:tools="http://schemas.android.com/tools"
        android:targetPackage="de.my.lib.test"
        tools:replace="android:targetPackage">
        <meta-data
            android:name="coverage"
            android:value="true" />
        <meta-data
            android:name="coverageFile"
            android:value="/sdcard/coverage.ec" />
    </instrumentation>
</manifest>

我尝试使用Gradle,但输出结果相同:

defaultConfig {
    // unimportant stuff
    testApplicationId "de.my.lib.test"

    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    testInstrumentationRunnerArgument('coverageFile', '/sdcard/coverage.ec')
}

最后我使用adb命令尝试了一下:

adb shell am instrument -w -e coverage true -e coverageFile /sdcard/coverage.ec de.my.lib.test/android.support.test.runner.AndroidJUnitRunner

但是我遇到了两个错误:

de.my.lib.utils.UtilsTest:. 找不到类: org.jacoco.agent.rt.internal_773e439.CoverageTransformer . 时间: 0.072

OK (1 测试)

错误: 无法生成 emma 覆盖率.

我完全迷失了。有任何想法吗?

背景为什么我需要将其存储在另一个位置:在某些设备和 Android 版本上使用 adb shell run-as 命令存在问题,因此我的测试设备农场中的某些设备返回 0% 的覆盖范围,因为无法拉取文件。所以我需要将文件存储在公共可用的位置。

3个回答

3

然而,覆盖率报告尚未生成。要启用此选项,我们需要向调试构建变体添加一个属性。使用Android插件DSL,我们可以通过testCoverageEnabled属性启用覆盖率:

android {
    ...
    buildTypes {
        debug {
            testCoverageEnabled true
        }
        ...
    }
}

当使用Android Gradle插件版本2.2.+ 时,如果要在本地测试中启用覆盖率报告,则需要在应用的build.gradle文件中启用它:

android {
    //...

    testOptions {
        unitTests.all {
            jacoco {
                includeNoLocationClasses = true
            }
        }
    }
}

Jacoco执行的仪器化生成了包含创建报告所需数据(HTML、XML等)的执行文件。问题在于Espresso生成.ec文件,而单元测试执行生成.exec文件...我们有不同的格式!由于我们无法配置Espresso的覆盖率任务,因此我们必须确保它首先被执行。接下来,我们需要运行单元测试,然后使用两个文件(ecexec)创建覆盖数据。为了实现这一点,我们需要再次编辑任务,并将coverage.ec文件作为executionData属性的参数添加进去。
apply plugin: 'jacoco'

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

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

    def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
    def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
    def mainSrc = "${project.projectDir}/src/main/java"

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

由于Android Gradle插件2.2.+现在为每个执行生成覆盖文件,并在文件名中使用设备/模拟器,因此我们现在需要将每个文件传递给执行数据,因为文件名现在是动态的。 此外,我将createDebugCoverageReport任务添加为我们自定义任务的依赖项,因此我们不需要手动运行它:) 最后,当我们首先运行Espresso测试,然后执行两个任务时,我们将获得统一的覆盖报告!
gradle clean jacocoTestReport

详细解释请参见这里

编辑:

因此,命令adb shell am instrument不会生成报告,为此,您必须使用gradle解决方案处理它。如果您真的想自己测试它,则必须使用自定义运行程序

参考资料:

1)如何自动生成HTML格式的Android测试报告

2)如何在使用“adb shell am instrument”时检索测试结果

3)https://github.com/jsankey/android-junit-report


从命令行进行测试


更新为命令行方法,请按照这些步骤进行。

运行插装应用程序

一旦我们有了EmmaInstrumentation类(在引用链接中),我们需要进行更多的调整,以便能够获取正在运行的应用程序的覆盖率报告。

首先,我们需要将新的Activity添加到清单中。其次,如果我们决定生成覆盖率报告,则应允许我们的应用程序写入sdcard。要做到这一点,您应该授予android.permission.WRITE_EXTERNAL_STORAGE权限。 然后,是时候构建和安装插装APK了:

$ ant clean
$ ant instrument
$ ant installi

一切准备就绪,可以启动受监控的应用程序了。

$ adb shell am instrument -e coverage true \
    -w com.example.i2at.tc/\
        com.example.instrumentation.EmmaInstrumentation

这里是源代码。

这里


不是通过 Gradle 的方式,而是通过“adb shell am instrument…”的方式。 - TheLittleNaruto
我们的想法是通过命令行获取它,因为我们正在测试系统应用程序。它使用mk文件生成构建,而不像通常使用gradle脚本的方式。 - TheLittleNaruto
@TheLittleNaruto,我已经更新了我的回答,请查看是否有帮助。 - user1773603
感谢您付出了很多努力。但是我们不使用ant。而且您提到的任何内容,我们都已经尝试过了。 - TheLittleNaruto
我找到了根本原因。已发布答案。请查看一下。 - TheLittleNaruto

1
对于我们来说,这是一个AOSP应用程序。我们生成测试和模块APK时禁用了Jack编译器,这导致了进一步的问题。因为对于我们来说,需要Jack编译器生成coverage.em。但错误并不是很相关。所以没有线索继续下去。
解决方法是运行Jack编译器服务器并使用它生成APK。

1
这个答案与问题中的错误信息有什么关系?没有一个单词提到coverage.em未被找到,但是提到了CoverageTransformer未被找到。 - Martin Zeitler
请再次阅读答案。@MartinZeitler,错误与问题中相同。 - TheLittleNaruto
@TheLittleNaruto,我看到你的回答是关于应用程序发布后的事情,而问题中并没有提到这一点;它只是关于调试应用程序,所以我的答案是基于这个问题的。顺便说一句,我很高兴,你解决了你的问题。 - user1773603

0

缺失的CoverageTransformer类提示了对JaCoCo代理运行时缺失依赖项。一个简单的解决方法可能是,提供它所需的(在设备上可用):

testImplementation "org.jacoco:org.jacoco.agent:0.8.3"

如果你在解决了ClassNotFoundException后遇到了FileNotFoundException,请确保有写入SD卡的权限,这似乎是另一个可能的陷阱:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

我刚刚看到,还有一个org.jacoco.report包,可以选择添加:

testImplementation "org.jacoco:org.jacoco.report:0.8.3"

org.jacoco.agent 包始终包含 CoverageTransformer 类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接