android单元测试作用,关于 Android 单元测试

article/2025/10/9 8:37:55

本文不会用各种高大上的理由试图去说服你写单元测试,只是描述笔者在单元测试这条路上一路走来的思考和简单的示例,如果顺便能让你觉得单元测试其实也没那么遥远、回头也在实际项目中尝试一下,估计就是本文最大的收获了。

一、提起单元测试,你对它的映像是什么?

大部分同学,可能都不了解单元测试,在实际项目中觉得这根本就是在浪费时间:我撸代码快的飞起,撸完交给 QA 测试就好,没有必要、也不用做单元测试,真的很多余,况且项目中一向都是这么干没发现啥问题。

另一部分同学,多少听说过或者稍微了解过单元测试,认为单元测试或许很重要但其实不知道重要在什么地方,当然也不知道怎么去写、到底哪些部分要写单元测试。

最后,差点漏了已经把单元测试应用在实际项目中且驾轻就熟的同学,为你们鼓掌!嗯,这篇文章不太适合你们。

笔者就是经历过从不了解到了解再到应用的过程。

刚开始工作那一两年干前端,因为不是科班出身,根本没怎么听过单元测试,后来开源项目见多了(基本上是国外的),才知道还有单元测试这个东西,但觉得离自己很遥远,团队也没有要求写。

随着对 TDD 等概念的了解,特别是转型移动开发后,官方开发者网站上都有单独的篇幅重点介绍怎么测试(当然也包括了单元测试),越发觉得它或许很重要,但并没有深入的理解,当然也不知道我们到底为什么要写单元测试,看上去还增加了额外的工作量。

当项目越做越大,开发者、代码量也越来越多,慢慢就会暴露出一些问题,也是笔者在实际项目中遇到的痛点(若站在团队的高度考虑问题,你对笔者的这些痛点会更有同感)。

第一个痛点是人,为什么这么说呢?因为人是不靠谱的,如果你花一下午一边喝咖啡一边写完了一个功能模块,一跑 0 bug,包括各种边界和异常你都全部考虑到了,那你是大神。但飞机也有失误的时候,更何况大神呢,大神也有状态不好的时候,所以,是人写出来的代码他都会有 bug,我们的目的是如何去减少它,保证代码质量。我们现在都是寄希望于 QA,整个周期太长,想找个 QA/开发坐在你旁边结对编程吧,代价太高别人还不愿意!而单元测试恰恰可以帮助我们做到这点,它就像是一个趟在硬盘里的 QA 机器人,随叫随到,实时提供质量保障服务,想想都有点高大上!

第二个痛点是编译,我想 Android 开发深有体会,编译时间太长了,修改一个 bug 后运行,打完水回来还在 building。如果只是小问题,这么来来回回太浪费时间了,时间就是生命啊!

第三个痛点是边界,有些边界在真机测试中是很难构造的,借助单元测试可以突破条件的限制,做为一个有经验且略带强迫症的开发我们理应不漏掉任何边界。

第四个痛点是重构,随着代码质量意识逐渐提升撸代码功力也在不断加强,业务也在不断发展变化,其实你会发现不少可以重构的模块,想对某段代码下手,又因为不知道影响边界而放弃。好不容易改了吧,其实心里是没有底的,都要 QA 去测试,但 QA 做的是黑盒测试,有一些异常情况我们开发很容易想出来但是 QA 很难测到,无形中增加了心理负担。

为了解决以上痛点,笔者开始了解 Android 单元测试,并运用在实际项目中。

二、什么是 Android 单元测试?

单元测试就是针对程序最小单元进行正确性校验的工作

来看看 Android 官方的测试金字塔

da9f3a481746

App 测试金字塔

它把 Android App 所涉及到的测试分成了三类,从下往上分别是单元测试,集成测试和 UI 测试。

单元测试是可以在本地快速执行的,组件可以通过 mock 生成。其特点是快!

集成测试只能跑在虚拟机或者真机,集成了多个系统组件,如无法 mock 的组件—相机调用,可用于单个页面的逻辑正确性测试。其特点是慢!

UI 测试就是模仿用户真实行为的测试,涉及完整业务流程,这个我们最为熟悉,每次提测 QA 都在手动或者自动做这部分工作。

不同的测试类型,侧重面不同,性价比也不同,官方推荐的测试比重是 7:2:1

单元测试是测试每个方法的正确性,只要保证每个方法都没有问题,那么由这些方法组成的模块也不会有太大问题(出问题要么是边界没考虑全要么是流程有问题),一定程度上起到减少 bug 率的作用,实际项目中已经不记得多少 QA 提的 bug 都是人为疏忽导致得了,通过单元测试都可以轻易避免啊喂。

Bug 少了,人也精神了,简直不要太爽!

三、我们在写 Android 单元测试时到底在测什么?

首先,不知道你是否也有这样的疑惑:单元测试都是针对纯 Java 的,Android 开发和系统组件有着千丝万缕的联系,所以 Android 项目中能写单元测试的类不多。笔者过去一段时间都是持这种观点的,大部分 Android 开发同学也不乏这么想的。

其实不然,借助第三方库如 Robolectric 同样可以像纯 Java 类一样去测试那些依赖系统组件的业务类。

之所以有“依赖系统组件的类不能单元测试”的误解,官方也有一定的责任,Android Studio 创建的项目默认只有 JUnit4 单元测试示例,要测试系统组件依赖的类,官方的示例是通过 AndroidTest,这是要跑在真机或者虚拟机上的,不是真正意义上的单元测试。

也就是说 Android 开发中所有的类都可以被单元测试。

其次,我们测试的目的有这几个方面,统称为 Right-BICEP

Right – Are the results right? 结果是否正确?

B – are all the boundary conditions correct? 所有边界条件都是正确的么?

I – can you check the inverse relationships? 能否检查一下反向关联?

C – can you cross-check results using other means? 能够使用其他手段交叉检查一下结果?

E – can you force error conditions to happen? 是否可以强制错误条件产生?

P – are performance characteristics within bounds? 是否满足性能要求?

三、Android 单元测试要怎么做?

要明确一点是,单元测试是一门需要学习的技术,无论单元测试、集成测试还是 UI 测试,他们都分别有自己的技术栈。如果你觉得单元测试需要花很多时间或者无从下手,或许是因为你对这门技术掌握得还不够多不够熟练,再者可能项目也没有很好的测试框架方便我们去写测试代码。

同时先介绍两个概念:Mock,这是 Android 单元测试最重要的概念,Mock 是指模拟出一个虚拟对象,替换我们原先依赖的真实对象,避免类之间相互影响。另外一个重要概念是 Shadow,是指在 Android SDK 类基础上封装一层影子类(如 Activity 和ShadowActivity、TextView 和 ShadowTextView 等),这些影子类,丰富了系统类的行为,提供测试接口。

关于测试理论和技术已经有很多成熟的资料,笔者也写不出什么新的花样,也不是本文的目的所在,这里不在做过多讲述,直接给出结论。

JUnit4 是纯 Java 单元测试框架,在创建项目时,Android Studio 已经搭建好,我们直接使用即可。

Mokito 是用来 Mock 依赖的类或者接口,对那些不容易构建的对象用一个虚拟对象来代替。

Robolectric 则在 JVM 中实现了 Android SDK 运行的环境,让我们无需运行虚拟机/真机就可以跑单元测试。

JaCoCo、GitLab 用来搭建单元测试报告平台,可实现定期运行、自动采集、错误报警,提供覆盖率、通过率数据查看,后面笔者会专门写一篇文章进行介绍。

我们来看看 JUnit4、Mokito、 Robolectric 在项目中如何使用,在 module 下的 build.gradle 文件中进行配置:

// 单元测试 JUnit(由 IDE 自动创建)

testImplementation 'junit:junit:4.12'

// 单元测试 Mokito

testImplementation "org.mockito:mockito-core:2.18.3"

// 单元测试 Robolectric

testImplementation "org.robolectric:robolectric:3.8"

就这么几行代码,Android 单元测试基础环境搭建完成,可以说很简单了。

我们用简单的例子说明它们的用法。

假设我们有 Math 类,提供 add(int a, int b) 方法:

da9f3a481746

待测类

想给 add 方法写测试用例,Android Studio 中的快捷方式是,对这个类进行右键 -> Go to -> Test。

da9f3a481746

创建测试方法(1)

点击 Create New Test。

da9f3a481746

创建测试方法(2)

在弹出框中,勾选对应方法,然后点击 OK 按钮,其他保持不变。

da9f3a481746

创建测试方法(3)

在目录选择弹框中,选择 ../test/... 目录,这个才是单元测试的目录,目录的选择决定了是单元测试还是集成测试。然后点击 OK。

da9f3a481746

创建测试方法(4)

单元测试类/方法就创建好了。

da9f3a481746

测试类

接下来我们写测试代码,其中 Assert 断言和 @Test 注解就用到了 JUnit。

da9f3a481746

测试代码

点击测试方法左侧的小三角(对方法右键也行),选择 Run xxx,很快就有结果。

da9f3a481746

运行测试用例

da9f3a481746

�测试用例运行结果

上述例子说明了如何创建测试类/方法,以及简单的 JUnit 用法,我们来改造一下 Math#add 方法:参数改成 String 类型,在相加之前先调用 Validator 对象判断所传入的参数是否为数字,而 Validator 通过 init 方法传进来,目的是为了可测试(有时为了可测试,我们要调整编码思维,本例中的处理方式只是为了说明问题,并非最优方案,也并非通用方案)。

da9f3a481746

改造后的 add 方法

这种情况 add 方法依赖了一个外部类 Validator,我们要测试其正确性就要排除依赖的影响,这样做的目的是错误隔离,也就是 Validator 自身的 bug 不会影响到 add 方法,Validator 由其对应的测试用例去保证。

隔离的好处是如果 add 测试用例运行失败,那么就能确定问题出在 add 方法中,和 Validator 没什么关系。

如何排除依赖类的影响呢?这时候就要请出 Mockito 库了。我们来看看测试用例怎么写。

da9f3a481746

通过 Mock 隔离依赖

具体直接看注释,这就是 Mockito 使用的例子。

另外一个例子是,我们有一个 Activity,上面有一个输入框、一个按钮、一个 TextView,输入框输入人名 Peter, TextView 输出 Hello, Peter! 截图如下。

da9f3a481746

界面示例

因为依赖系统组件,我们用 Robolectric 写测试用例。

da9f3a481746

Robolectric 单元测试

同样请直接看注释。

以上就是 JUnit、Mockito、Robolectric 组合使用的示例,为了便于理解(引人入坑),举的例子都比较简单,实际应用中其实可以玩出很多花样。


http://chatgpt.dhexx.cn/article/FPRxpLZk.shtml

相关文章

分享基于安卓项目的单元测试总结

前言: 负责公司的单元测试体系的搭建,大约有一两个月的时间了,从最初的框架的调研,到中期全员的培训,以及后期对几十个项目单元测试的引入和推进,也算是对安卓的单元测试有了一些初步的收获以及一些新的认…

java 线程 js_js javascript 实现多线程

在讲之前,大家都知道js是基于单线程的,而这个线程就是浏览器的js引擎。 首先来看一下大家用的浏览器都具有那些线程吧。 假如我们要执行一些耗时的操作,比如加载一张很大的图片,我们可能需要一个进度条来让用户进行等待&#xff0…

js创建多线程

我们知道js是单线程的,如果把占用大量计算资源的代码,或者获取大量数据,耗时较多的请求分离出去,用一个单独的线程去处理,会不会提升页面的性能的,答案是肯定的,尤其是现在我们开发大多是单页应…

javascript-js实现多线程

在讲之前,大家都知道js是基于单线程的,而这个线程就是浏览器的js引擎。 首先来看一下大家用的浏览器都具有那些线程吧。 假如我们要执行一些耗时的操作,比如加载一张很大的图片,我们可能需要一个进度条来让用户进行等待&#xff…

html5多线程例子,javascript的单线程事件循环及多线程介绍

前言 其实我前面文章对于改变js的执行顺序及多线程都有相关介绍!例如,我们可以用setTimeout(fn,0)改变代码执行循序,文章最后也提及了Event Loop(事件循环)。同时,js的Worker可以模拟实现多线程,我前面文章也有类似的应…

js 实现多线程

2019独角兽企业重金招聘Python工程师标准>>> 一、多线程理解 首先,我们要理解什么是多线程,百度百科上说:多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线…

javascript多线程

一、什么是多线程 二、Concurrent.Thread.js <meta charset"utf-8" /> <script src"Concurrent.Thread.js"></script> <script src"https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script><style&…

js 多线程Worker

任务执行优先级&#xff0c;微任务&#xff08;Promise&#xff09;>宏任务&#xff08;定时器&#xff09;>线程任务&#xff08;Worker&#xff09; 多线程处理多个任务&#xff0c;这里假设5个任务循环了80亿&#xff0c;如果按之前js单线程执行是不是5*80亿&#xf…

Javascript的单线程与多线程

目录 一、浏览器的线程和进程 1.浏览器的线程 2.浏览器是多进程的 二、Javascript是单线程的 1.异步Ajax也是单线程的 2.setInterval和setTimeout本质上并不是多线程 三、Web Worker支持多线程 1.多线程间数据交互 2.Web Worker的兼容性 3.Web Worker的使用限制 3.1同…

JavaScript多线程编程

浏览器端JavaScript是以单线程的方式执行的&#xff0c;也就是说JavaScript和UI渲染占用同一个主线程&#xff0c;那就意味着&#xff0c;如果JavaScript进行高负载的数据处理&#xff0c;UI渲染就很有可能被阻断&#xff0c;浏览器就会出现卡顿&#xff0c;降低了用户体验。 …

JavaScript多线程初步学习

一、多线程理解 首先&#xff0c;我们要理解什么是多线程&#xff0c;百度百科上说&#xff1a;多线程&#xff08;英语&#xff1a;multithreading&#xff09;&#xff0c;是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时…

JavaScript如何实现多线程?

今天看到一道面试题&#xff0c;问js如何实现多线程?下面来总结一下&#xff1a; 因为 JS是一种单线程语言&#xff0c;即使是一些异步的事件也是在JS的主线程上运行的。像setTimeout、ajax的异步请求&#xff0c;或者是dom元素的一些事件&#xff0c;都是在JS主线程执行的&a…

JS多线程

JS是多线程的吗&#xff1f; 多线程编程相信大家都很熟悉&#xff0c;比如在界面开发中&#xff0c;如果一个事件的响应需要较长时间&#xff0c;那么一般做法就是把事件处理程序写在另外一个线程中&#xff0c;在处理过程中&#xff0c;在界面上面显示类似进度条的元素。这样…

at24c16如何划分出多个读写区_mega32数组、内存以及AT24C16读写相关

主控&#xff1a;mega32 编译器&#xff1a;iar2.31E 这两天折腾一个模块程序&#xff0c;一个温度补偿参数&#xff0c;本来是72个字节&#xff0c;现在扩展了三倍&#xff0c;变成288个&#xff0c;然后各种问题出现了。 第一次修改时想当然&#xff0c;直接把两个用到的全局…

STC8H开发(十二): I2C驱动AT24C08,AT24C32系列EEPROM存储

目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解)STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解)STC8H开发(三): 基于FwLib_STC8的模数转换ADC介绍和演示用例说明STC8H开发(四): FwLib_STC8 封装库的介绍和使用注意事项STC8H开发(五…

STM32的硬件I2C与AT24C16

刚学STM32的时候就听闻STM32的硬件I2C存在重大bug&#xff0c;会导致运行卡死在等待ACK的过程中&#xff0c;所以一直以来对其避而远之&#xff0c;转而以模拟I2C取代之。最近这段时间一直在用STM32 CubeMX&#xff0c;图形化设置界面屡试不爽&#xff0c;连USB这种复杂外设都能…

STM32F030 硬件I2C驱动 AT24C16

网络上很多F1系列的ATC24的读写程序&#xff0c;但F0几乎没有。由于F0完全重写了I2C&#xff0c;所以以往的代码并不能直接使用&#xff0c;修改事件、接口上会浪费很多时间&#xff0c;特别是对于使用F0系列进行入门的新手。 在此十分感谢 畅学电子网 的对于AT24C16的资料&am…

EEPROM 之 AT24C16 - 备忘录

因为论坛里看到STM的I2C有点小bug&#xff0c;所以这里采用的是模拟I2C时序 【注】m0.6us表示的是这一段时间最小不能小于为0.6us&#xff0c;M0.6us表示的是这一段时间最大为0.6us 对AT24C16的操作有读和写&#xff0c;读又分为CURRENT ADDRESS READ、RANDOM READ、SEQUENTIAL…

S32K144:12.LPI2C驱动AT24C16

1.打开官方例程 2.修改引脚配置 3.时钟可按照实际情况修改&#xff0c;也可不用更改&#xff0c;本例时钟不做更改 4.配置LPI2C模块 设置从机地址&#xff1a;从机地址如下图所示&#xff0c;低三位表示为AT24C16的块地址&#xff0c;AT24C16将2KB的内存空间分为8个块&…

stm32cubemx I2C读取AT24C16

本文对如何使用stm32cube生成I2C工程不作说明&#xff0c;仅对在对AT24Cxx系列的使用时作出易忽略的说明&#xff1b; 1、at24cxx页面结构&#xff1a; 从该图可以看出16K&#xff08;bit&#xff09;共有128个页&#xff0c;每页由16byte构成。16k 128 * 16 * 8; 特别注意&…