共计 3464 个字符,预计需要花费 9 分钟才能阅读完成。
什么是 UT?
UT(Unit Test)即单元测试
UT 有什么价值?
大部分的开发都不喜爱写 UT,起因无非以下几点:
- 产品经理天天催进度,哪有工夫写 UT
- UT 是测试本人的代码,自测?那要 QA 何用?
- 自测能测出 bug?都是基于本身思维,就像考试做完第一遍,第二遍查看一样,根本查看不出什么货色
- UT 保护老本太高, 投入产出比太低
- 不会写 UT
总之有无数种理由不想写 UT,作为工作不到三年的菜鸟深有体会。之前在点评工作的时候,团队的“UT”都集中于 RPC 的服务端。
为啥带双引号?因为 RPC 的服务端没有页面能够功能测试,部署到测试环境测试太麻烦,只能写 UT 了。在这个场景下我认为叫“验证”更适合,验证不等于测试。验证往往只写主逻辑是否通过,且就一个 Case,且没有 Assert,有的是 System.out。
自己实习的时候做测试的,那时候晓得一个测试模型。如下图:
图的意思就是越底层做的测试成果越好,越往上则越差。也就是说大部分公司当初做的功能测试其实是成果最差的一种测试形式。另外,QA 界有个现场:大家都晓得功能测试没技术含量,那如何使本人突出呢?答案就是:自动化测试。事实是没几个公司能做好自动化测试,业界做的比拟好的百度算一个。
那么为啥自动化测试这么难做的?在这个模型当中,越往上黑盒越大,自动化测试难度就越大。这句话反过来就是越往下自动化测试就越好做?没错,UT 其实是最容易实现且成果最好的自动化测试。所以在很多公司呈现一种现场:QA 写 UT。
起因总结一下就两点:开发不违心写 UT,QA 想自动化测试解放本人。以上的模型只是实践上阐明 UT 具备微小的价值,然而真的如此么?我只想说,只有真正尝到 UT 的益处的苦头才会意识到 UT 的价值。
Unit Test & Intergration Test
单元测试和集成测试的界限我置信大部分开发也是不清晰的。集体了解单元测试针对于一块业务逻辑最小的单元,太形象。物理上能够简略了解为一个类的办法,能够是 public 办法也能够是 private 办法。一个单元测试不应该蕴含内部依赖的逻辑,反之就是集成测试了。
问题的外围就在于此。
一个 service 的一个接口实现可能依赖很多第三方:
1. 本地其它的 service
2.dao 调用
3.rpc 调用
4. 微服务调用。
如下图:
也就是说你的单元测试,真正调用了内部依赖那就是集成测试。这其实很常见对不?咱们先说这种状况下如何集成测试。
Local Integration Test
本地集成测试也就是说不依赖与其余过程。包含:service 依赖其余本地 service 或者 dao 的状况。在讲述如何集成测试之前,咱们先理一下测试模型,测试次要蕴含三块内容:1. 数据筹备 2. 执行逻辑 3. 输入验证。
第一步:数据筹备
在本地集成测试里,数据起源基本上来自于 dao,dao 来自于 sql。也就是在执行一个 case 之前,执行一些 sql 脚本,数据库则应用 h2 这类 memory database,切记不要依赖公司测试环境的 db。
下图是应用 spring-test 框架的一个 case,能够在 case 执行之前筹备咱们所须要的各种数据,另外在执行完 case 之后,执行 clean.sql 脚本来清理脏数据。这里也阐明一个 case 的执行环境是齐全独立的,case 之间互不烦扰,这很重要。
第二步:执行逻辑最简略,就是调用一下咱们测试的办法即可
第三步:验证
集成测试个别是调用 service,或者 dao 的接口验证。
举个例子:CRUD 操作的集成测试
- 调用 C 接口
- 调用 R 接口,验证 C 胜利
- 调用 U 接口
- 调用 R 接口,验证 U 胜利
- 调用 D 接口
- 调用 R 接口,验证 D 胜利
Remote Integration Test
假如咱们一个 service 实现依赖某个 RPC Service
第一步:数据筹备
跑到他人家的数据库插几条数据?或者跟 PRC Service 的 Owner 磋商好,搭一个测试环境供咱们测试?有些公司还真有专门的自动化测试环境,那么即便有测试环境,那如何实现各种 case 场景下,第三方 Service 很配合的返回数据给咱们?想想都蛋疼。
第二步:执行办法
假如咱们胜利的解决了第一步中的问题,大快人心。当初来看第二步,假如咱们的 service 外面调用了另一个 RPC Service 创立了很多数据,跑了无数次 case,后果 ….RPC Service 对应的数据库都是咱们的脏数据,如何清理?而且他们敢轻易删数据吗?想想也蛋疼。
第三步:输入验证
假如咱们又欢快的解决了第二步中的问题。当初来看第三步,假如咱们的办法执行最终输入是创立了一个订单,订单当然是调用订单 Service 接口了,那么咱们如何验证订单是否胜利创立了呢?或者能够调用订单 Service 查问订单的接口来验证。很显著大多数状况下并没有这么完满。想想也蛋疼呀。
通过以上剖析,Local Integration Test 是可行的,Remote Integration Test 根本不可行。
那么有没有什么方法解决呢?答案就是 Mock
- 第一步:Mock RPC Service 想返回什么数据就返回什么数据
- 第二步:还是 Mock 接口,想调用几次就调用几次
- 第三步:这一步等到上面讲完单元测试就明确了
Unit Test
下面咱们谈到 Mock 能够解决内部依赖的问题,当初有很多 Mock 的开源框架比方:mockito。那么问题来了,既然咱们能够 mock 第三方近程依赖,为何不 mock dao、local service 呢?没错内部依赖全副 mock 掉,就是单元测试了。因为咱们只关怀所测试的办法的业务逻辑,也就是真正高内聚的逻辑单元了。如下图:
益处如下:
- 没有什么数据是造不进去的,统统返回 Mock 的对象
- 代码中的异样解决代码,也能够通过 mock 接口,使之抛出异样
- 不产生任何脏数据
- 跑 case 更快了,因为不必启动整个我的项目,相当于 Main 办法
有人会说,都 mock 了还测试个蛋蛋。
这就是对于单元测试的了解了,单元测试应该只针对于指标办法的业务逻辑测试,dao、其它 service 应该在它们本身的单元测试去测试。对于依赖的第三方,咱们应该信赖它们能正确的实现咱们所预期的。这句话很难了解对不对?
举几个例子
例子一:办法的最初是执行 dao 的 create 操作,那么该如何验证?
咱们应该验证的内容是:
- dao 的 create 办法被调用了
- 调用次数是对的
- 调用参数也是对的
没错,只有这三个验证通过,那么这个 case 执行就是通过的。因为咱们置信 dao 的 create 操作能正确的实现咱们所预期的,只有咱们调用了正确的次数并且参数都是对的。
dao 的执行的正确性保障是在该 dao 的单元测试做的。在 Remote Integration Test 外面第三步验证情理是一样的,咱们应该验证 RPC 接口被调用了且次数和参数都是对的,那么咱们的 case 就算通过了,至于,RPC 服务端是否正确执行是它们的事件不是咱们所关怀的。Mockito 框架的 verify 接口就是做这件事件的。如果你了解了上述内容,那么你就开窍了,UT 不在变得这么难写。
什么时候用单元测试,什么时候用集成测试?
在自己的实际中摸索发现,对于简略的业务,比方 crud 型的瘦 service,比拟适宜于集成测试。
以下状况适宜于单元测试:
- Util 类
- 含有近程调用的办法
- 输出少,业务逻辑简单的办法
- 须要异样解决的办法
case 细到什么水平为好?
这个问题也是比拟经典的,一个办法要是所有的门路都笼罩到,那么要写很多的 case,说真的累死人。我的倡议是两个准则:1. 外围逻辑,容易出错的逻辑肯定要笼罩到 2. 依据本人的工夫。没必要写的十分多,毕竟 case 保护老本很高,业务逻辑一改,case 得跟着改。
总结
自己目前在从事于开源我的项目 (Apollo(配置核心) ) 研发,开源我的项目对代码品质要求相对来说高一些,UT 当然是很重要的一环。刚开始也不会写 UT,当然态度上也不器重 UT。
老大的代码 UT 覆盖率很高,抱着对开源负责的态度缓缓承受学习 UT,到起初尝了几次苦头后,发现 UT 真的很实用,价值也很高,然而很遗憾 UT 被大部分开发所疏忽。当然自己对 UT 的了解、实际还不够,仍需持续实际模式。
最初说一句:当开发完性能,跑完 UT,你能够释怀的上线了的时候,你的 UT 就胜利了。
近期热文举荐:
1.1,000+ 道 Java 面试题及答案整顿(2021 最新版)
2. 别在再满屏的 if/ else 了,试试策略模式,真香!!
3. 卧槽!Java 中的 xx ≠ null 是什么新语法?
4.Spring Boot 2.5 重磅公布,光明模式太炸了!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!