Loading [MathJax]/jax/input/TeX/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一文带你解决Android app手工测试或者自动化测试覆盖率统计(撸代码版)

一文带你解决Android app手工测试或者自动化测试覆盖率统计(撸代码版)

作者头像
雷子
发布于 2021-03-15 08:23:40
发布于 2021-03-15 08:23:40
1.7K00
代码可运行
举报
运行总次数:0
代码可运行

我们经常会遇到这样的问题。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1.手工测试覆盖率是多少?
2.UI自动化覆盖率是多少?
3.你怎么保证你覆盖了全部的场景?

其实这三个问题不难回答,可以从两个维度,

1.覆盖了需求的是多少,用例评审时,就是一个很好的统计。如果不到,会有补充,但是这个人为因素多,可能不全面。

2.看下功能测试或者UI自动化测试对于app 的代码的覆盖度是多少?

要想看到这个,我们必须要用工具呢,有了工具,我们才很好的去度量呢。我们选择Jacoco。那么如何来做呢。接下来,我们一起去解密,如何统计app 代码覆盖率。

首先,我们要在安卓代码中引入我们的依赖。在我们待测app的build.gradle做如下配置,引入我们的jacoco。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
apply plugin: 'jacoco'
jacoco {
    toolVersion = "0.8.4" #依赖版本号
    description("$buildDir/filescoverage.exec")#覆盖率文件的路径
    reportsDir = file("$buildDir/reports/jacoco")#测试报告路径

}

配置完毕后,Android studio自动去给我们加载包。

接下来就是代码去实现了。我们去创建一个接口,FinishListener。接口主要有两个方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface FinishListener {
    void onActivityFinished();
    void dumpIntermediateCoverage(String filePath);
}

我们新起一个InstrumentedActivity,这个的目的呢,开始收入代码覆盖数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class InstrumentedActivity extends LoginActivity {
    public static String TAG = "InstrumentedActivity";

    private FinishListener mListener;

    public void setFinishListener(FinishListener listener) {
        mListener = listener;
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        super.finish();
        if (mListener != null) {
            mListener.onActivityFinished();
        }
    }
}

那么我们接下来去实现一个JacocoInstrumentation。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class JacocoInstrumentation extends Instrumentation implements
        FinishListener {
    public static String TAG = "JacocoInstrumentation:";
    private static String DEFAULT_COVERAGE_FILE_PATH = "coverage.ec";


    private final Bundle mResults = new Bundle();

    private Intent mIntent;
    private static final boolean LOGD = true;

    private boolean mCoverage = true;

    private String mCoverageFilePath;

    @Override
    public void onCreate(Bundle arguments) {
        Log.d(TAG, "onCreate(" + arguments + ")");
        super.onCreate(arguments);

        File file = new File(getContext().getFilesDir(),"coverage.ec");
        System.out.println(file.getAbsolutePath());
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                Log.d(TAG, "异常 : " + e);
                e.printStackTrace();
            }
        }
        if (arguments != null) {
            mCoverageFilePath = arguments.getString("coverageFile");
        }

        mIntent = new Intent(getTargetContext(), InstrumentedActivity.class);
        mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        start();
    }

    @Override
    public void onStart() {
        if (LOGD)
            Log.d(TAG, "onStart()");
        super.onStart();

        Looper.prepare();
        InstrumentedActivity activity = (InstrumentedActivity) startActivitySync(mIntent);
        activity.setFinishListener(this);
    }

    private void generateCoverageReport() {
        Log.d(TAG, "generateCoverageReport():" + getCoverageFilePath());
        OutputStream out = null;
        try {
            out = new FileOutputStream(getContext().getFilesDir()+getCoverageFilePath(), false);
            Object agent = Class.forName("org.jacoco.agent.rt.RT")
                    .getMethod("getAgent")
                    .invoke(null);

            out.write((byte[]) agent.getClass().getMethod("getExecutionData", boolean.class)
                    .invoke(agent, false));
        } catch (Exception e) {
            Log.d(TAG, e.toString(), e);
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private String getCoverageFilePath() {
        if (mCoverageFilePath == null) {
            return DEFAULT_COVERAGE_FILE_PATH;
        } else {
            return mCoverageFilePath;
        }
    }

    private boolean setCoverageFilePath(String filePath) {
        if (filePath != null && filePath.length() > 0) {
            mCoverageFilePath = filePath;
            return true;
        }
        return false;
    }


    @Override
    public void onActivityFinished() {
        if (LOGD)
            Log.d(TAG, "onActivityFinished()");
        if (mCoverage) {
            generateCoverageReport();
        }
        finish(Activity.RESULT_OK, mResults);
    }

    @Override
    public void dumpIntermediateCoverage(String filePath) {
        // TODO Auto-generated method stub
        if (LOGD) {
            Log.d(TAG, "Intermidate Dump Called with file name :" + filePath);
        }
        if (mCoverage) {
            if (!setCoverageFilePath(filePath)) {
                if (LOGD) {
                    Log.d(TAG, "Unable to set the given file path:" + filePath + " as dump target.");
                }
            }
            generateCoverageReport();
            setCoverageFilePath(DEFAULT_COVERAGE_FILE_PATH);
        }
    }
}

这里,我们用到的就是统计覆盖的数据,最后生成文件,最后的文件生成是对应activity 销毁。

到这里,我们还需要去配置我们的响应的权限,因为要用到对应的权限。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

我们还需要吧对应的activity加载进来。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 <activity android:label="InstrumentationActivity"
            android:name="com.example.studayappp.test.InstrumentedActivity" />

由于是基于instrumentation,我们还需要对instrumentation进行相关的配置。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 <instrumentation
            android:handleProfiling="true"
            android:name="com.example.studayappp.test.JacocoInstrumentation"
            android:functionalTest="false"
            android:label="tihis test"
            android:targetPackage="com.example.studayappp">
        </instrumentation>

配置完毕,我们就可以打包,然后用adb 执行下面的命令,去启动app

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
adb shell am instrument 包名/test.JacocoInstrumentation

启动app后,就可以正常测试。最后,我们返回或者杀掉应用。就可以产生对应的文件,路径在下面

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/data/data/yourPackageName/files/coverage.ec

然后我们去pull下我们的覆盖率文件即可。最后呢,我们利用app的build.gradle配置一个任务即可

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def coverageSourceDirs = [
        '../app/src/main/java'
]
task jacocoTestReport(type: JacocoReport) {
    group = "Reporting"
    description = "Generate Jacoco coverage reports after running tests."
    reports {
        xml.enabled = true
        html.enabled = true
    }
    classDirectories = fileTree(
            dir: './build/intermediates/javac/debug/classes',
            excludes: ['**/R*.class',
                       '**/*$InjectAdapter.class',
                       '**/*$ModuleAdapter.class',
                       '**/*$ViewInjector*.class'
            ])
    sourceDirectories = files(coverageSourceDirs)
    executionData = files("$buildDir/filescoverage.exec")

    doFirst {
        new File("$buildDir/intermediates/javac/debug/classes/").eachFileRecurse { file ->
            if (file.name.contains('$$')) {
                file.renameTo(file.path.replace('$$', '$'))
            }
        }
    }
}

如果配置中无法识别task任务中的方法的,可能是因为版本不一样,我的版本如下

这样我们去执行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gradlew.bat   jacocoTestReport

就可以产生对应的测试报告了。

如果我们经过手工测试, 出来一个这样的报告,我们就可以告诉我的覆盖率是多少。那么反过来,我们也会发现,原来我们的用例也有覆盖不全的地方,即使我们经过用例的评审的阶段,还会出现覆盖不到的地方。但是我们满足了业务的100%覆盖,还有未覆盖的,我们需要斟酌覆盖的投入产出比。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 代码覆盖率100% 不代表没有bug。代码没有覆盖100% 一定有bug

但是有可能你覆盖到80% 很轻松,往后增加5% 都费很大劲。那么我们可以去没有覆盖到的进行分析。不一定要做到代码100%全覆盖,尤其在功能测试阶段,代码100% 覆盖,会给大家增加很多的工作量,很有可能为了1%的覆盖率而耽误整体测试,得不偿失。覆盖率是为了提升我们测试用例的覆盖度,检验我们测试用例设计的全面性,它有两面性,合理引入覆盖率,合理选择一定的阈值。

本文介绍了Jacoco统计安卓app手工测试覆盖率的方法,这里没有做增量代码的覆盖率,没有做多人分工测试app,测试报告如何合并,如何启动不用Instrumentation直接启动app。后续的文章中,将会持续分享。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 雷子说测试开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
安卓app功能或自动化测试覆盖率统计(干货进阶版)
今天是国庆节假期的第四天,这是假期的第一篇技术文章。再次祝大家节日快乐。准备收收心,回去工作了。
雷子
2021/03/15
1.3K0
安卓app功能或自动化测试覆盖率统计(干货进阶版)
简单两步实现 Jacoco+Android 代码覆盖率的接入!(最新最全版)
JaCoCo的概念我就不在这里复述了网上有很多资料介绍,这里主要提一下他的两种插桩模式:On-the-fly和Offline
岛哥的质量效能笔记
2021/08/18
6.6K0
简单两步实现 Jacoco+Android 代码覆盖率的接入!(最新最全版)
Android+jacoco实现代码覆盖率最正确的实现方式,没有之一!
在我接到这个需求,需要统计开发人员提交代码自测率的时候,从其他渠道和gradle推荐了解到的实现方式都是jacoco,然后也上网查了不少的资料,网上的资料都非常老了,gradle插件依赖的不是1.+就是2.+,gradle依赖还是4.4左右,所以导致一个问题,也是浪费了我很多时间的问题:网上的资料已经跟不上时代了,然而没有一篇最新的、最正确的jacoco+Android集成实践的博文,来给有这方面有诉求的同学指引方向,在我费尽千辛万苦终于找到突破口并实现了之后,决定记录这个问题,为日后有需求的同学点一盏明灯!
全栈程序员站长
2022/09/13
3.7K1
Android+jacoco实现代码覆盖率最正确的实现方式,没有之一!
Android如何精准化测试?
最近搞了一个基于jacoco统计Android代码覆盖率测试的功能,可以统计每天手工测试的代码覆盖率.自己也学习一下jacoco,陆陆续续搞了三天终于有点结果了.
测试加
2022/03/24
7280
Android如何精准化测试?
Android自动化测试探索(六)代码覆盖率统计
之前在 https://www.cnblogs.com/zhouxihi/p/11453738.html 这篇写了一种统计Android覆盖率的方式
周希
2019/12/11
7680
Android自动化测试探索(五)代码覆盖率统计
同样如果以上说的几个都不懂也行, 让开发帮忙做这些然后编个代码覆盖率统计的包给你测试, 测完把手机给开发取数据生成报告。 注意每次测试完先返回手机桌面把程序退到后台等几秒让app自己生成日志文件
周希
2019/10/15
1.2K0
Java代码覆盖率历史发展轨迹
作为一个测试人员,保证产品的软件质量是其工作首要目标,为了这个目标,测试人员常常会通过很多手段或工具来加以保证,覆盖率就是其中一环比较重要的环节。
JavaEdge
2020/05/27
1.4K0
如何达成100%的测试覆盖率?
测试覆盖率是一种度量指标,指的是在运行一个测试集合时,代码被执行的比例。它的一个主要作用就是告诉我们有多少代码测试到了。其实更严格地说,测试覆盖率应该叫代码覆盖率,只不过大多数情况它都是被用在测试的场景下,所以在很多人的讨论中,并不进行严格的区分。
架构狂人
2023/09/24
4.7K0
如何达成100%的测试覆盖率?
Android app 功能代码覆盖率--Jacoco(二)
1.在项目根目录下,进入dos,运行:gradlew.bat jacocoInit,会再app下生成code-voerage文件夹
厦门-安仔
2019/09/16
1.8K0
Android app 功能代码覆盖率--Jacoco(二)
Android app 功能代码覆盖率--Jacoco(三)
前面两篇都是讲了jacoco配合Andorid app 代码覆盖的配置以及单人测试生成覆盖率测试报告,那遇到多人测试一个版本,要怎么合并,来评估这个版本的测试范围跟测试质量,这才比较实用;这个就是今天要说的内容 ~其实也很简单,就是下载不同的jacoco 覆盖率配置文件,该文件已被修改过,可以合并多份.ec文件并对比生成一份报告;
厦门-安仔
2019/09/23
1.4K0
Android app 功能代码覆盖率--Jacoco(三)
如何知道我们的E2E测试覆盖率?
在单元测试中,很容易知道已经覆盖了哪些代码区域。但是我们能及时知道API调用的动态范围吗?我们一直在思考,既然已经编写了许多 E2E 测试用例,但是我们应该继续编写多少剩余测试?
ThoughtWorks
2020/09/25
1.6K0
super-jacoco单元测试覆盖率度量实践-1
代码覆盖率,尤其是增量代码覆盖率,是质量门禁的重要指标之一。由于一些不可名状的原因,团队原先提供质量门禁服务的工具暂时停服了,因此需要另外寻找一个工具来代替提供此项服务。于是,笔者就Super-Jacoco做了一个简单的POC。
Antony
2022/02/08
1.7K0
super-jacoco单元测试覆盖率度量实践-1
从精准化测试看ASM在Android中的强势插入-JaCoco初探
在Java技术栈上,基本上提到覆盖率,大家就会想到JaCoco「Java Code Coverage的缩写」,几乎所有的覆盖率项目,都是使用JaCoco,可想而知它的影响力有多大,我们在Android项目中,也集成了JaCoco,官网文档如下。
用户1907613
2021/09/08
3.3K0
从精准化测试看ASM在Android中的强势插入-JaCoco初探
Android增量代码测试覆盖率工具
美团点评业务快速发展,新项目新业务不断出现,在项目开发和测试人员不足、开发同学粗心的情况下,难免会出现少测漏测的情况,如何保证新增代码有足够的测试覆盖率是我们需要思考的问题。
Java架构师必看
2020/04/10
2.3K0
统计代码测试覆盖率-Python
衡量Unit Test(单元测试)是否充分, 覆盖率是一个必要指标, 是检验单元测试的重要依据, 这里针对python unittest 的单元测试覆盖率coverage进行分享.
louiezhou001
2019/07/24
1K0
统计代码测试覆盖率-Python
教你使用 Jacoco 统计服务端代码覆盖率
前面有一篇 文章 使用 Python + Coverage 来统计测试用例的代码覆盖率
AirPython
2020/07/29
3.6K1
教你使用 Jacoco 统计服务端代码覆盖率
增量代码覆盖率工具
目前有赞共享技术团队测试介入的微服务应用有几百个,大部分底层应用的单测覆盖率在 70% 以上,同时测试组提供的多纬度集成测试自动化的覆盖率也在 70% 以上。有赞的业务发展非常快,当存量代码较多时,新项目功能测试的整体覆盖率偏低是正常现象,另外开发提测时,并不能依据已有的全量覆盖率来判断对新增代码的自测完成度,基于这个背景,我们研发了增量代码覆盖率工具,作为项目质量的参考纬度之一,支持统计功能测试、单测和集成测试,并集成到了 DevOps 平台。
有赞coder
2020/08/25
2.3K0
增量代码覆盖率工具
基于 Jenkins + JaCoCo 实现功能测试代码覆盖率统计
对于 JaCoCo,有所了解但又不是很熟悉。 "有所了解"指的是在 CI 实践中已经使用 JaCoCo 对单元测试代码覆盖率统计: 当代码 push 到代码仓库后,用 JaCoCo 进行单元测试代码覆盖率统计,并将相应数据推送到 SonarQube。 "不是很熟"指的是应用场景也仅限于此,并未进行过多研究与实践。
LinuxSuRen
2019/05/23
4.4K0
Jacoco统计接口测试的代码覆盖率
搜狗商城现有的接口自动化测试框架是使用Python搭建的,共900多条case,每天都会运行一次,从而监控是否有因开发代码变更或者新功能添加而导致的遗漏的bug。但我们只是依照测试用例来转换成自动化脚本、case,实际上并没有度量的指标,也不能保证测试的完整性,所以我们打算引入代码覆盖率这一指标来度量测试完整性。
用户5521279
2019/08/09
3.9K1
Jacoco统计接口测试的代码覆盖率
Android中根据coverage.ec文件生成报告
Android手工测试代码覆盖率增强版 Android手工测试的代码覆盖率 Android UI自动化测试的代码覆盖率
全栈程序员站长
2022/09/13
9130
Android中根据coverage.ec文件生成报告
推荐阅读
相关推荐
安卓app功能或自动化测试覆盖率统计(干货进阶版)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验