Android代码单元测试

Android代码单元测试

1. 目的

  • 单元测试是白盒测试,能够验证具体的功能模块逻辑是否正确
  • 保证代码质量,对代码逻辑进行方法级别的测试,确保每个功能模块的逻辑正确
  • 在良好的单测覆盖率之下,项目能够在保证质量的前提下进行快速迭代

2. 测试用例覆盖范围

一个待测试页面对应多个业务单元,每个业务单元对应一个测试用例,每个测试用例都是一条链路(比如点击一个按钮后产生的一系列直接影响如页面跳转等),实现条件覆盖和路径覆盖。
在Android单元测试中,测试用例不需要覆盖所有的方法逻辑(这也是不太现实的),应该专注于自己编写的业务逻辑等代码的测试覆盖,像 Android SDK 里的方法回调是不需要测试的。

3. 怎样测试逻辑方法

3.1 目标方法有明确的返回值

编写单元测试时调用这个方法,验证返回值是否符合预期

3.2 目标方法本身没有返回值,但是会改变一些对象的(属性||状态)

调用该方法,然后验证方法改变的对象的(属性||状态)是否符合预期

3.3 目标方法没有返回值,也不会改变对象

调用该方法,然后验证其行为,比如按钮的点击事件,验证是否弹出Toast、是否有弹框、是否有页面跳转等

4. 技术选型

  • JUnit断言
  • Mockitomock数据,数据解耦
  • Robolectric在JVM上运行单测,不需要模拟器or真机
  • Jacoco统计测试覆盖率,便于补充完善单测

5. GET STARTED

  1. 我们的单元测试基于Robolectric,因为它直接运行于JVM之上,运行单测时速度更快,不需要准备Android环境,不用使用真机||Android模拟器运行测试。当需要进行依赖解耦时,可以使用Mock框架
  2. 单元测试主要是针对逻辑进行测试,所以就需要App项目的架构清晰,可测试性好,将UI和逻辑分开,不然的话,还是先重构吧
  3. 测试代码在app/src/test目录下

5.1 单元测试环境配置(已完成)

  • Robolectric && JUnit:在app/build.gradle中引入
dependencies {
    
    XXXXXX
    
    testCompile 'junit:junit:4.10'
    testCompile 'org.robolectric:robolectric:3.2.2'
    testCompile 'org.robolectric:shadows-multidex:3.2.2'
    
    XXXXXX
}
复制代码
  • Mock配置:在app/build.gradle中引入
dependencies {

    XXXXXX

    testCompile "org.mockito:mockito-core:1.+"
    
    XXXXXX
}
复制代码
  • jacoco:在app/build.gradle中引入
apply from: 'http://git.caimi-inc.com/client/jacoco-plugin/raw/master/jacoco-plugin.gradle'
复制代码
  • 友情提示:在第一次用Robolectric跑单测时,会下载一堆依赖,可能会比较慢

5.2 新建||更新测试用例文档

  1. 如果是给新页面编写单测,需要新建一个单测文档,放在docs/单元测试下:比如是WacaiLoginActivity页面的单测,则文档名为WacaiLoginActivity用例列表
  2. 根据单测文档模板编写对应的测试用例,更新该单测用例文档

5.3 编写测试用例代码

  1. 根据测试用例文档编写测试用例代码
  2. 一个页面||工具类对应一个测试类,用例文档中一个测试用例对应一个测试用例方法

5.4 查看单测覆盖率,以便完善单测

  1. 进入项目所在目录,执行./gradlew jacocoTestReport
  2. 执行成功之后,会在目录app/build/reports/jacoco生成详细的覆盖率报告,使用浏览器打开index.html即可查看
  3. jacoco执行成功之后,同时会将报告上传到pages上,查看地址:pages.wacai.info/client/andr…
    1. 其中,APP_VERSION表示当前应用的版本号versionCode
  4. 如果执行失败,则说明有单测跑不过,需要进行检查,可能原因:测试用例写得有问题、被测试的逻辑代码有问题、测试框架(配置||环境)有问题
    1. 测试用例有问题:修改测试用例,使之符合正确的逻辑
    2. 被测试的逻辑代码有问题:fix代码逻辑问题
    3. 测试框架(配置||环境)问题:修改配置or环境
  5. 查看单测覆盖率,包括分支覆盖率和行覆盖率
    1. 分支覆盖率:条件执行时不同的输入条件导致的不同逻辑走向,最常见的是if-else两个分支,分支覆盖率是指被测试代码覆盖的分支数占总体分支的比例,体现的是不同输入条件下代码各种边界情况的覆盖情况
    2. 行覆盖率:被测试代码覆盖的逻辑代码行数占总体逻辑代码行数的比例,是一个总体的数值,是一个比较绝对的值
  6. 根据jacoco生成的测试报告不断完善测试用例,直至覆盖所有的分支、边界等。jacoco生成的测试报告非常直观,能够很容易地看出哪些部分的代码被覆盖到了,哪些没有被覆盖到,很方便我们完善测试用例

5.5 文档式单测流程图

6. 示例:挖财宝-挖财账号登录页单元测试实践

为了介绍单元测试的实施过程,下面以挖财宝挖财账号登录页作为单元测试实践案例。该案例的开发和测试涉及到了TextView, EditText, Button, Checkbox, ImageView,包含了各种点击、页面跳转等逻辑。页面如下图所示

对页面进行单元测试的时候,我们首先需要分析页面,针对页面提取出业务逻辑,提取出的业务逻辑如上图所示。根据这些逻辑来设计单元测试的case(带有Test注解的被测试方法),业务逻辑包括需求中的业务以及其他的需要维护的代码逻辑。为了减少单元测试case的维护成本,业务流程不允许跨页面,以页面为基本单位。

挖财账号登录页的单元测试case设计如下:

目标页面业务覆盖界面元素逻辑描述最小断言数case名称
挖财账号登录页 WacaiLoginActivity点击左上角返回1. 左上角TextView控件1. 点击左上角TextView控件,关闭结束登录页1testGoBack
输入账号、密码1. 账号输入框(手机/邮箱/账号)
2. 账号输入框右侧清空图标ImageView
3. 密码输入框EditText
4. 密码输入框右侧隐藏密码图标ImageView
1. 向账号输入框输入内容
2. 向密码输入框输入内容
3. 当账号输入框没有内容时,账号输入框右侧清空图标ImageView隐藏
4. 当账号输入框有内容时,账号输入框右侧清空图标ImageView显示
5. 当密码输入框没有内容时,隐藏密码图标隐藏
6. 当密码输入框有内容时,隐藏密码图标显示
7. 点击清空图标,账号输入框文本清空
8. 点击隐藏密码图标,密码输入框文本清空
6testInputAccountAndPassword
点击登录1. 登录按钮
2. 账号输入框
3. 密码输入框
1. 点击登录按钮
2. 账号密码输入框为空,则弹出Toast,页面不跳转
3. 账号不为空,密码为空,弹出Toast,页面不跳转
4. 账号密码均不为空,正常登录逻辑
4testLogin
自动填充上次登录用户的用户名1. 账号输入框 2. 密码输入框当用户曾经登录过时,进入页面会自动在账号输入框填充上一次登录成功用户的用户名:
1. 填充账号输入框
2. 填充之后,密码输入框获得焦点
2testPastePreAccount
直接点击跳转1. 右上角注册TextView控件
2. 忘记密码TextView控件
3. 挖财平台注册协议TextView控件
4. 挖财隐私权政策TextView控件
1. 点击注册跳转到手机号注册登录页面
2. 点击忘记密码跳转到找回密码页面
3. 点击挖财平台注册协议或者挖财隐私权政策,跳转到对应的WebView
4testJumpDirectly
点击切换是否同意挖财注册协议1. 同意挖财注册协议Checkbox勾选框
2. 登录按钮
1. 勾选同意挖财注册协议,则登录按钮可点击;否则不可点击
2. 进入页面时默认勾选同意挖财注册协议
3testAgreeRegisterProtocol

接下来需要在单元测试的工程中实现上述case。最小断言数是从业务逻辑考虑的一个数值,并不是代码的边界条件,真实的case需要考虑代码的各种边界情况,比如空指针等,因此,一般实际断言数会大于最小断言数。实际断言数=最小断言数(业务需求断言)+技术需求断言。

写完case之后需要跑一遍单测得到单测报告,根据单测报告不断完善单测,提高单测覆盖率。

7. 其他问题

  • Robolectric 3.+在各个场景下如何编写测试用例,请查看这里
  • 使用Robolectric 3.+遇到的常见问题汇总,请查看这里

8. 总结

  • 单元测试不是一个能够产生直观可见效益的工程,但是对于保障快速迭代时的代码质量具有重要意义。试想一下,面对一个单元测试覆盖率很高的工程,如果你要进行部分重构的话也会更有信心吧?你每一点对代码的改动,都有可能对其他部分的业务逻辑产生影响,有了单元测试,代码对逻辑的影响可以通过运行单测来判定,如果单测覆盖完备&&单测运行通过,那么就可以认为改动对现有业务逻辑没有影响
  • 项目架构很影响单元测试的实施,层次分明、代码解耦、可测试性好的代码在编写单测的时候是非常舒畅的。如果项目中UI和逻辑高度耦合,代码逻辑都堆在Activity中,那么,你最需要的可能不是单测,而是对项目的重构,各个模块进行解耦、UI和逻辑解耦等
  • 编写代码的时候需要适当考虑代码的可测试性

9. 最后

以上就是文档式单测的内容,目前正在实践之

10. 参考资料

转载于:https://juejin.im/post/5c8f4a155188252da179defe

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值