Android系统应用无法通过AS得到覆盖率报告

最近公司要求所有应用都要补充单元测试,AS对单元测试支持非常到位,只需在build.gradle中设置testCoverageEnabled为true即可(无需其他设置):

进行gradle同步,会得到两个新的task:

只需执行这两个task(一个是仪器测试、一个是本地测试),便可一键执行用例并生成html报告。

现象

但是我的测试uid为system的系统应用时,执行完成后,得到的报告中并没有覆盖率(全部为0),后来用了一个简单的demo测试:

不设置sharedUserId为system时一切正常:

找了半天也没看到准确的答案和解决方法,无奈只能自己动手。

原因

通过研究发现,是得到的覆盖率文件coverage.ec大小为0,但是通过adb shell到设备上查看是有正常的coverage.ec的,于是通过命令行加上--debug参数执行:gradlew app:createDebugAndroidTestCoverageReport --debug,果然发现了报错:

Shell command failed (1): run-as xxx cat "/data/data/xxx/coverage.ec" > "/data/local/tmp/12035a40-3985-484c-8cdf-ad5b4240d316-coverage_data/coverage.ec"
run-as: package not an application: xxx

原来,createDebugAndroidTestCoverageReport并不是通过adb pull直接把coverage.ec拖出来,而是通过run-as命令,现将shell的uid切换到被测应用的uid,然后就可以访问应用的data的文件了,再通过cat重定向到/data/local/tmp下的临时文件中

而在系统源码中,在platform/system/core/run-as/run-as.cpp有这样一句:

  // Reject system packages.
  if (userAppId < AID_APP) {
    error(1, 0, "package not an application: %s", pkgname);
  }
所以系统uid是不能run-as的。

解决方法

那只能曲线救国了,先通过命令行执行测试,并adb pull取出coverage.ec,然后手搓一个jacoco.gradle的脚本,放到项目根目录下,用来解析pull出来的coverage.ec,得到html报告:

apply plugin: 'jacoco'

android {
    buildTypes {
        debug {
            testCoverageEnabled = true
        }
    }
}

// 源代码路径,从module路径起算,有多少个module,就在这里写多少个路径,如果你只有app一个module,那么就写一个就可以
def coverageSourceDirs = [
        '/src/main/java',
]

// class文件路径,从module路径起算,如果你只有app一个module,那么就写一个就可以
def coverageClassDirs = [
        '/build/intermediates/javac/debug/classes'
]

// systemuid的应用,无法直接执行createDebugAndroidTestCoverageReport生成html报告(普通应用可以)
// 需使用命令行运行测试用例,然后通过adb pull设备手动取出coverage.ec,放到outputs\code_coverage\debugAndroidTest\connected\coverage.ec
// 最后手动执行gradlew jacocoTestReport生成报告
task jacocoTestReport(type: JacocoReport) {
    group = "JacocoReport"
    description = "Generate coverage reports after running tests."
    reports {
        xml.required = false
        html.required = true
        csv.required = true
    }

    classDirectories.from = files(coverageClassDirs)
    sourceDirectories.from = files(coverageSourceDirs)
    executionData.from = files("$buildDir/outputs/code_coverage/debugAndroidTest/connected/coverage.ec")
}

把这个jacoco.gradle导入导模块的build.gradle中:

apply from: rootProject.file('jacoco.gradle')

这样,就会多出一个叫jacocoTestReport的gradle task,在执行完用例并pull了coverage.ec后,执行该任务即可(记得coverage.ec文件路径要对应)

搞定!!!

注意:

一定不要在AS右侧的task中执行测试任务,那个执行完后会卸载包,那么coverage.ec文件也就被删除了,需要命令行执行:

adb shell "am instrument -w -m -e coverage true -e coverageFile /data/data/xxx/coverage.ec xxx.test/androidx.test.runner.AndroidJUnitRunner"

xxx为你的被测应用包名

  • 46
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值