什么是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开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞+转发哦!