给安卓开发小白们的unit test指南 - 这也能测?这也要测?

@Test(expected = IllegalStatusException.class)
public void sizeTooLargeAssertException(){
activity.display(30000)
}

原来我们在activity里面有逻辑,限制最多只能展示大于10000的size,如果我在运行app之前就已经实现跑过了HouseActivity 的单元测试,我就会提前知道原来我们的app不处理大于10000的数据。

以上只是一个简单的例子,但是这个例子说明了一个很大的问题,就是在提交你的代码之前,运行一个有效的单元测试是有多么重要。他可以帮你测试修改的代码会对其他模块有什么影响,如果破坏了既有的测试(规则),你应该怎么处理。要知道很多代码在修改之后,你以为你打开app手动测试一下通过了肯定就没问题,但是你有没有想过,这个代码,这个类,会不会对其他页面有影响。这个就是单元测试的作用:

制定一套既有的规则,所有新增/修改的代码要按照这个规则来运行。

测试这种规则,要比你手动打开app测试更加健壮且快速(compile 一个完整app vs 运行 一个纯java的测试)。

在理想状态下,每一个类的每一行代码都要被unit test cover,一套单元测试的coverage(覆盖率)可以体现你给你代码制定规则的数量和健壮程度。比如说还是用上面的例子:

class Presenter{
enum Status{
LARGE,
MEDIUM
}

public Status getFinancialStatus(int size){
if(size < xxx){
return Status.SMALL
}
else{
return Status.MEDIUM
}
}
}

你的测试如果只有:

@Test
public void smallSizeReturnSmallStatus(){
int size = 90

assertThat(presenter.getFinancialStatus(size)).isEqualTo(Status.SMALL)
}

那你对Presenter这个类的coverage只有50%。为什么?因为你的test没有覆盖到else这个语句,补上一下测试:

@Test
public void largeSizeReturnLargeStatus(){
int size = 3000

assertThat(presenter.getFinancialStatus(size)).isEqualTo(Status.MEDIUM)
}

跑完这两个测试,你的presener的单元测试覆盖率就是100%了,恭喜!

顺便说一句,现在android studio已经支持显示unit test 覆盖率了,有兴趣可以看看

developer.android.com/studio/test

比如我有个dummy class

Screenshot 2021-05-02 at 3.42.08 PM.png

给它的else语句加个test:

Screenshot 2021-05-02 at 3.41.30 PM.png

Android Studio不仅会给出覆盖率等重要数据,还会给代码加上标记,这样开发者就可以轻易的看出来哪一行代码没有被测试覆盖,是否需要加测试(原谅色代表被覆盖,红色代表没有被覆盖)

Screenshot 2021-05-02 at 3.41.37 PM.png

测什么?

我们都知道一个类的单元测试是要保证这个类能正常运行。那么什么是类能正常运行呢?这个标准是什么?

还是以例子为主:

class HousePagePresenter{

//http service client
private HouseApiService service = new HouseApiService()

public Data getHouseData(int size){
if(size < 1000){
return service.call(Status.SMALL)
}
else{
return service.call(Status.MEDIUM)
}
}
}

HouseApiService 是一个做http call的类,参数是Status。当size小于1000就传SMALL,反之MEDIUM。那对于HousePagePresenter来说,这个类怎么样运行才是正确的?

那就是当getHouseData() 传入的参数小于1000的时候,service 类成员要调用call 方法,而且参数是SMALL,反之是MEDIUM。

HousePagePresenter只需要保证在合适的size的前提下,service能调用call并且使用正确的Status就行了。我们只在乎service有没有做出正确的动作,至于动作结果,不重要!

怎么测?

那说回来,这个怎么测?

首先,要给一个代码做测试,要先保证他是可测的。上面的代码其实是没法测试的!Not testable.因为HouseApiService作为私有对象,我们没办法模拟(Mock)它,从而无法验证它的行为在一定条件下是否符合我们的期望。

正确的做法是,要做“依赖注入”。把HousePagePresenter对因为HouseApiService的依赖,从类对象的方式转移成别的方式,或者说可测的方式,比如移到构造函数里面(也可以通过别的方式比如说setter)。

class HousePagePresenter{

public HousePagePresenter(HouseApiService service){
this.service = service
}

//http service client
private HouseApiService service;

public Data getHouseData(int size){
if(size < 1000){
return service.call(Status.SMALL)
}
else{
return service.call(Status.MEDIUM)
}
}
}

这样的好处可以说是非常大。这样,我们在测试HousePagePresenter类的时候,就不需要真正的创建一个HouseApiService了,而是可以模拟:

@Test
public void smallSizeServiceCall(){
int size = 900
HouseApiService service = mock(service.class)
HousePagePresenter presenter = new HousePagePresenter(service)

presenter.getHouseData(size)

//验证service是不是真正调用了call,并且参数也是期望值
verify(service).call(Status.SMALL)
}

通过把Service移到构造函数,让代码可以通过mockito mock的方式生成一个模拟的Service,这个service不会做任何真正的http call,只会记录自己call()方法被调用的情况。这就够了,这已经能证明HousePagePresenter这个类没问题,如果service有问题,那应该在service自己的单元测试里面解决。

具体怎么解决依赖注入,可以稍微看一下一个视频

www.bilibili.com/video/BV1e5…

有人觉得只有用dagger这类依赖注入库才叫依赖注入,这是一个常见的误解。想了解更多的朋友可以自行搜索一下。

安卓控件没法测?

很多朋友会说自己有很多逻辑需要安卓本身的控件支持,这部分真的没法测啊。乖乖,谷歌已经给我们提供了从UI到系统api的全家桶,想偷懒不写test?不存在的。。。

纯UI的单元测试

对于fragment 和 activity本身的UI测试,Roboletric 框架提供了ActivityRule支持,允许开发者在unit test中启动测试activity,从而启动fragment。同时配合Espresso框架可以再unit test代码中获取View对象,达到测试View的目的。

比如::

//设置测试activity类
private activityScenarioRule = ActivityScenarioRule(TestActivity.class)
@Before
void setup{
//启动测试fragment
activityScenarioRule.scenario.onActivity{
activity.setFragmemnt(new TestFragment());
}
}

@Test
void whenButtonClicked_executeMethod(){
// 通过onView获取button,手动模拟点击事件
onView(R.id.button).performClick();
verify(presenter).getHouseData()
}

结合ActivityScenarioRule和Espresso,我们可以把Fragment或者Activity当成一个正常的再正常不过的类来进行测试了。我刚刚入职谷歌的时候就想偷懒不给UI写test,找借口说UI测不了,直接被senior大哥焦作人。。。

系统API

假如你的方法里需要获得当前手机运营商信息,那你可能需要TelephonyManager这个系统api来帮忙。

fun getCarrierId(){
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声

开发是需要一定的基础的,我是08年开始进入Android这行的,在这期间经历了Android的鼎盛时期,和所谓的Android”凉了“。中间当然也有着,不可说的心酸,看着身边朋友,同事一个个转前端,换行业,其实当时我的心也有过犹豫,但是我还是坚持下来了,这次的疫情就是一个好的机会,大浪淘沙,优胜劣汰。再等等,说不定下一个黄金浪潮就被你等到了。

  • 330页 PDF Android核心笔记

  • 几十套阿里 、字节跳动、腾讯、华为、美团等公司2020年的面试题

  • PDF和思维脑图,包含知识脉络 + 诸多细节

  • Android进阶系统学习视频

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

PDF和思维脑图,包含知识脉络 + 诸多细节

[外链图片转存中…(img-EMFdKWTa-1712025009603)]

  • Android进阶系统学习视频

[外链图片转存中…(img-6ukgd0ni-1712025009603)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值