APP重构之路:引入单元测试
一、为什么要引入单元测试
在开发过程中我们会遇到这样一些问题:
- 面对需要重构庞大的模块代码时无从下手
 - 修改了一处地方却在另一处地方引发了新的bug
 - 扩展新功能的同时导致旧代码出现bug
 - 在测试人员难以覆盖到的基础功能接口出现了bug
 - 出现了一种难以重现的特殊边界条件触发的bug
 
另外我们也许还会遇到一些这样的模块:
- A模块依赖于B模块的结果,但是B模块尚未开发完成
 - 模块状态过于复杂,手工测试需要耗费大量时间
 - 模块业务与时间节点相关,手工测试难以覆盖
 
这个时候也许能够利用经验和丰富的debug技巧来解决这些问题,但是很多时候我们的处理并不完美,因为我们缺少了一个规范,在编码过程中难以顾及其他模块的影响,这个时候,我们就需要引入单元测试。

二、单元测试的价值
可维护性增强
当在对代码进行修改时,利用单元测试就能够清晰的知道是否破坏了老的业务逻辑,这样大大减少了回归出错的可能性。而当我们从测试那里获得了一个bug时,就可以通过测试用例去还原,当我们这个测试通过后,这个bug也就解决了,而且这个bug fix的测试用例也保证了这个bug以后不会再次出现。
降低重构难度
有了单元测试的保障,我们可以比较大胆的进行重构设计,而单元测试也会成为重构时很好的一个模具。当然在重构时也需要对单元测试进行重构,但是和可靠性相比,这种额外的负担是值得去承受的
减少调试时间
在调试中,我们很多时候都需要花费一些额外的时间来触发需要调试的代码,但是在单元测试中,我们能够针对需要调试的代码构建相关的测试用例,方便的进行反复的测试与模拟,大大减少了调试的时间。
减少低级错误
测试的存在价值就是为我们发现并解决错误,单元测试更是如此,当我们对自己的代码进行单元测试时,就能容易的排除掉一些非常低级的错误,起码我们能够保证在一些正常的情况下代码是可以正确工作的。
描述代码
好的代码就是一份好的文档,单元测试更是如此。一份好的单元测试能够描述在对应的情况下,代码应该有如何的预期表现,那么别人只需要查看测试用例就能清楚的知道代码的功能。
提高代码质量
一份代码如果和其他代码强耦合,它是难以被测试的,所以为了测试,开发人员会被驱使写出低耦合、可扩展的代码。

三、单元测试方案
单元测试中有测试驱动开发(TDD)与行为驱动开发(BDD)两种思路
测试驱动开发(TDD)
- 根据需求与接口先编写测试用例
 - 根据测试用例编写业务代码
 - 开发效率低
 - 资源耗费大
 - 测试覆盖率高
 
行为驱动开发(BDD)
- 通过测试用例描述代码行为
 - 通过自动运行测试用例快速反馈
 - 通过Mock作为相关代码模块的替身
 - 开发效率较高
 - 资源耗费较低
 - 测试覆盖率较TDD要低
 
基于目前项目的情况与开发流程,我选择了BDD作为测试框架,并会选择使用XCTest + OCMock + OCHamcrest的方案,以下是三个框架的介绍:
XCTest
XCTest 可以完成的事
- 基本断言的逻辑判断
 - 异步测试
 - 性能测试
 
为什么选择 XCTest
- XCode原生的测试框架,能够更好适应Apple之后的更新
 - XCTest有大量文档支持,上手难度较低
 - XCTest添加进项目后只是作为项目测试框架,并不会影响到打包等一些东西
 
OCMock
为什么需要 OCMock
mock即为模拟,OCMock可以伪造(模拟)一个对象,给它一些预设的值之类的,并进行对应的验证
比如在我需要测试WiFi直连模块时,我需要一个WiFi才能测试直连功能,这个时候我们就可以利用OCMock,去模拟一个WiFi对象,它可以是模拟成风险WiFi,也可以模拟成免费WiFi,这样我们的直连模块的测试就完全独立于WiFi对象,可以方便的进行测试。
OCMock 可以完成的事
- 创建一个模拟对象,模拟一个特定对象的行为,排除一些外部类的干扰
 - 构造自己的用例进行验证
 - 对已有方法进行重定义,以自己定义的逻辑进行交互
 - 判断函数是否执行过
 
为什么选择 OCMock
- 原生XCTest并不支持Mock功能
 - OCMock是专门为iOS与OS X进行Mock测试的开源项目,拥有超过5000+ app使用,1100万+下载量
 - OCMock使用Apache 2.0协议,能够在需要时候修改代码满足需要并作为开源或商用产品发布/销售
 - OCMock有官方文档,资料齐全
 
OCHamcrest
OCHamcrest 可以完成的事
- 更高级的断言
 - 断言可扩展性
 - 支持结构体的断言
 
为什么选择 OCHamcrest
- 相对于另一个断言框架 Expecta ,OCHamcrest更为成熟,Expecta可能会导致断言结果错误
 - XCTest 内置断言并不充分,复杂条件下的判断需要编写大量断言代码
 - 利用OCHamcrest的扩展性能够将格式化的自定义log输出到日志文件,提供更多可以定制化而且详细的信息
 
四、整体测试框架

五、应该测试什么 应该怎么测试
测试的原则
- 快速:这样才不会介意去运行
 - 独立:一个测试不应该耦合于另一个测试
 - 可重复:每次测试的结果应该一致
 - 可验证:结果应该是成功/失败,而不是一个解释性的日志文档
 
测试的内容
在写任何测试前,应该明确应该要测试什么,一般的情况下,单元测试应该包括这些内容:
- 核心功能测试
 - 边界条件
 - 错误处理
 
测试的思路
针对目前项目情况,我会使用单元测试与人工测试相结合的方式去进行,因为目前我们大部分功能都是与UI牵连,不能完全依靠单元测试去完成所有的测试工作,但是我们可以将逻辑部分进行分离,举网络连接模块为例:
请求流程

我们可以对界面相关的部分测试进行拆解,在逻辑部分实现单元测试,而人工测试部分单纯检查整个直连流程和界面部分是否正常。
这样能够避免人工测试时依赖于逻辑的情况,比如在需要测试发起100个请求后模块是否会出现问题时,无需依靠手工去真的连接100次,只需要在单元测试中模拟进行100次连接,并查看结果是否正确就可以。
应该测试的对象
在项目中,我们有大量的类,全部覆盖单元测试是不现实的,我们需要进行挑选。以下是我列举的一些因素。
1.数据相关
比如在本地数据存储模块中,我们需要保存不同的数据,这时候我们可以通过单元测试构造不同的测试数据进行保存,查看是否保存成功,数据部分是单元测试最需要覆盖的部分。
2.逻辑相关
比如在连接模块中,需要对部分请求结果进行过滤,这就是一个逻辑,针对这种逻辑,可以在单元测试中进行测试是否过滤成功,而人工测试则无需关注过滤的逻辑,仅仅需要关注过滤后界面是否正常显示。
3.多状态的模块
比如在连接模块中,连接的状态就有8种,包括了连通性检查、连接中、已连接等,这些状态能够利用单元测试很好的模拟出来,这样就解决了人工测试下难以模拟不同状态转换的问题。
六、总结
我们写代码最终的目的只有两个:实现需求与提高代码质量,在保证完成需求的前提下,增加单元测试能提高代码的质量与可维护性,纵使在引入了单元测试后,我们也许会面临增加了研发的代码量,花费更多精力在编写单元测试上,增加了开发成本,但我认为相比于单元测试带来的优势,这些是能够克服的。
以上内容就是本篇的全部内容以上内容希望对你有帮助,有被帮助到的朋友欢迎点赞,评论。
如果对软件测试、接口测试、自动化测试、面试经验交流。感兴趣可以关注我,我们会有同行一起技术交流哦。
APP重构之路:引入单元测试的更多相关文章
- 2020,最新Model的设计-APP重构之路
		
很多的app使用MVC设计模式来将“用户交互”与“数据和逻辑”分开,而model其中一个重要作用就是持久化.下文中设计的Model可能不是一个完美的,扩展性强的model范例,但在我需要重构的app中 ...
 - Hybrid框架UI重构之路:五、前端那点事儿(HTML、CSS)
		
上文回顾 :Hybird框架UI重构之路:四.分而治之 这里讲述在开发的过程中,一些HTML.CSS的关键点. 单页模式的页面结构 在单页模式中,弱化HTML的概念,把HTML当成一个容器,BODY中 ...
 - Hybrid框架UI重构之路:三、工欲善其事,必先利其器
		
上文回顾:Hybird框架UI重构之路:二.事出有因 工欲善其事,必先利其器,事是重构的目标,器是开发环境. 这篇文章将讲述重构时的UI框架的目录结构,且需要使用的开发工具. 目录结构 demo : ...
 - Android Studio重构之路,我们重新来了解一下Google官方的Android开发工具
		
Android Studio重构之路,我们重新来了解一下Google官方的Android开发工具 记得我的第一篇博客就是写Android Studio,但是现在看来还是有些粗糙了,所有重构了一下思路, ...
 - (转)Android项目重构之路:实现篇
		
前两篇文章Android项目重构之路:架构篇和Android项目重构之路:界面篇已经讲了我的项目开始搭建时的架构设计和界面设计,这篇就讲讲具体怎么实现的,以实现最小化可用产品(MVP)的目标,用最简单 ...
 - (转)Android项目重构之路:界面篇
		
在前一篇文章<Android项目重构之路:架构篇>中已经简单说明了项目的架构,将项目分为了四个层级:模型层.接口层.核心层.界面层.其中,最上层的界面,是变化最频繁的一个层面,也是最复杂最 ...
 - Hybrid框架UI重构之路:六、前端那点事儿(Javascript)
		
上文回顾 :Hybird框架UI重构之路:五.前端那点事儿(HTML.CSS) 这里讲述在开发的过程中,一些JS的关键点. 换肤 对于终端的换肤,我之前一篇文章有说了我的想法. 请查看:http:// ...
 - Hybrid框架UI重构之路:四、分而治之
		
上文回顾:Hybird框架UI重构之路:三.工欲善其事,必先利其器 上一篇文章有说到less.grunt这两个工具,是为了css.js分模块使用的.UI框架提供给使用者的时候,是一个大的xxx.js. ...
 - Hybrid框架UI重构之路:二、事出有因
		
上文回顾:Hybird框架UI重构之路:一.师其长技以自强 一切的重构都是有原因的,或许为了更快速度.更好体验.更快捷开发等,于是就有了自己的开发目标,简单看看未重构前UI("中国移动式&q ...
 
随机推荐
- Python编程第四版中文 上下册完整版pdf|网盘下载附提取码
			
点击此处下载 提取码:drjh 作者简介 Mark Lutz是Python培训的世界的领先者,他是最初和最畅销的Python著作的作者,从1992年起就是Python社区的先锋人物.Mark有25年的 ...
 - luogu4443 coci 2017 Dajave
			
题目 给出一个长度为2^M的排列,元素分别是0, 1, 2, ... , 2^M -1. 选择其中某个非空连续子序列,然后允许交换这个排列中某两个不同的数,然后使得这个连续子序列的所有数的按位异或(b ...
 - hdu6787(骰子+往回推的传输带问通过方案,dp)
			
题:http://acm.hdu.edu.cn/showproblem.php?pid=6787 题意:有1~n标号的格子,上面有m个传输带(传送带传的位置要传到之前去,1位置不能有格子)1~11的骰 ...
 - 认识SpringData JPA
			
简介 JPA全称Java Persistence API,中文名是Java持久层API.用来描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中. 名词解释 RDS:关系型数据库服务 Re ...
 - Markdown上手
			
Markdown 学习日记 标题 二级标题 两个#+空格 快捷键:Ctrl + 2 三级标题 三个#+空格 快捷键:Ctrl + 3 最多支持六级标题 字体 加粗 文字 两个 *+文字+两个 * 快捷 ...
 - [深度学习] Pytorch学习(二)—— torch.nn 实践:训练分类器(含多GPU训练CPU加载预测的使用方法)
			
Learn From: Pytroch 官方Tutorials Pytorch 官方文档 环境:python3.6 CUDA10 pytorch1.3 vscode+jupyter扩展 #%% #%% ...
 - SOAR安全能力编排化
			
安全能力编排化(Security Capability Orchestration)是指系统一方面可以通过自底向上地通过安全设施接口化和安全接口应用化实现安全应用编排化:另一方面则自顶向下地将安全运营 ...
 - LeetCode 115.不同的子序列 详解
			
题目详情 给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数. 一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串.(例如, ...
 - 怎么写简历,简历才不会被丢到非洲🌍
			
前言 只有光头才能变强. 文本已收录至我的GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3y 最近的三歪朋友圈可以看到很多的字节.腾讯的同学都 ...
 - markdown基础使用技巧
			
markdown基础使用技巧 通过``创建代码形式,不同形式可以叠加(比如:斜体+加粗) 块级元素 通过return/Enter实现切换段落/创建段落 通过shift+return/enter 实现换 ...