点击关注公众号,Java干货及时送达
UT(Unit Test)即单元测试
总之有无数种理由不想写UT,作为工作不到三年的菜鸟深有体会。之前在点评工作的时候,团队的“UT”都集中于RPC的服务端。
(图一)
图的意思就是越底层做的测试效果越好,越往上则越差。也就是说大部分公司现在做的功能测试其实是效果最差的一种测试方式。另外,QA界有个现场:大家都知道功能测试没技术含量,那如何使自己突出呢?答案就是:自动化测试。现实是没几个公司能做好自动化测试, 业界做的比较好的百度算一个。
那么为啥自动化测试这么难做的?在这个模型当中,越往上黑盒越大,自动化测试难度就越大。这句话反过来就是越往下自动化测试就越好做?没错,UT其实是最容易实现且效果最好的自动化测试。所以在很多公司出现一种现场:QA写UT。
原因总结一下就两点:开发不愿意写UT,QA想自动化测试解放自己。以上的模型只是理论上说明UT具有巨大的价值,但是真的如此么?我只想说,只有真正尝到UT的好处的甜头才会意识到UT的价值。
单元测试和集成测试的界线我相信大部分开发也是不清晰的。个人理解单元测试针对于一块业务逻辑最小的单元,太抽象。物理上可以简单理解为一个类的方法, 可以是public方法也可以是private方法。一个单元测试不应该包含外部依赖的逻辑,反之就是集成测试了。
问题的核心就在于此。最新面试题整理好了,点击Java面试库小程序在线刷题。
一个service的一个接口实现可能依赖很多第三方:
1.本地其它的service
2.dao调用
3.rpc调用
4.微服务调用。
如下图:
(图二)
也就是说你的单元测试,真正调用了外部依赖那就是集成测试。这其实很常见对不?我们先说这种情况下如何集成测试。Spring Boot 单元测试教程推荐你看下。
推荐一个 Spring Boot 基础教程及实战示例:https://github.com/javastacks/spring-boot-best-practice
(图三)
假设我们一个service实现依赖某个RPC Service
点击关注公众号,Java干货及时送达
跑到别人家的数据库插几条数据?或者跟PRC Service的Owner商量好,搭一个测试环境供我们测试?有些公司还真有专门的自动化测试环境,那么即使有测试环境,那如何实现各种case场景下,第三方Service很配合的返回数据给我们?想想都蛋疼。
假设我们成功的解决了第一步中的问题,皆大欢喜。现在来看第二步,假设我们的service里面调用了另一个RPC Service创建了很多数据,跑了无数次case,结果....RPC Service对应的数据库都是我们的脏数据,如何清理?而且他们敢随便删数据吗?想想也蛋疼。
假设我们又愉快的解决了第二步中的问题。现在来看第三步,假设我们的方法执行最终输出是创建了一个订单,订单当然是调用订单Service接口了,那么我们如何验证订单是否成功创建了呢?
或许可以调用订单Service查询订单的接口来验证。很明显大多数情况下并没有这么完美。想想也蛋疼呀。通过以上分析,Local Integration Test是可行的,Remote Integration Test基本不可行。
那么有没有什么办法解决呢?答案就是Mock
上面我们谈到Mock可以解决外部依赖的问题,现在有很多Mock的开源框架比如:mockito。那么问题来了,既然我们可以mock第三方远程依赖,为何不mock dao、local service呢?
没错外部依赖全部mock掉,就是单元测试了。因为我们只关心所测试的方法的业务逻辑,也就是真正高内聚的逻辑单元了。如下图:
(图四)
好处如下:
有人会说,都mock了还测试个蛋蛋。
这就是对于单元测试的理解了,单元测试应该只针对于目标方法的业务逻辑测试,dao、其它service应该在它们自身的单元测试去测试。对于依赖的第三方,我们应该信任它们能正确的完成我们所预期的。这句话很难理解对不对?
举几个例子
推荐一个 Spring Boot 基础教程及实战示例:
https://github.com/javastacks/spring-boot-best-practice
例子一:方法的最后是执行dao的create操作,那么该如何验证?
我们应该验证的内容是:
没错,只要这三个验证通过,那么这个case执行就是通过的。因为我们相信dao的create操作能正确的完成我们所预期的,只要我们调用了正确的次数并且参数都是对的。
dao的执行的正确性保证是在该dao的单元测试做的。在Remote Integration Test里面第三步验证道理是一样的,我们应该验证RPC接口被调用了且次数和参数都是对的,那么我们的case就算通过了,至于,RPC服务端是否正确执行是它们的事情不是我们所关心的。Mockito框架的verify接口就是做这件事情的。如果你理解了上述内容,那么你就开窍了,UT不在变得这么难写。
什么时候用单元测试,什么时候用集成测试?
在本人的实践中摸索发现,对于简单的业务,比如crud型的瘦service,比较适合于集成测试。以下情况适合于单元测试:
这个问题也是比较经典的,一个方法要是所有的路径都覆盖到,那么要写很多的case,说真的累死人。
我的建议是两个原则:
1、核心逻辑,容易出错的逻辑一定要覆盖到
2、根据自己的时间。没必要写的非常多,毕竟case维护成本很高,业务逻辑一改,case得跟着改。
本人目前在从事于开源项目(Apollo(配置中心) )研发,开源项目对代码质量要求相对来说高一些,UT当然是很重要的一环。刚开始也不会写UT,当然态度上也不重视UT。
老大的代码UT覆盖率很高,抱着对开源负责的态度慢慢接受学习UT,到后来尝了几次甜头后,发现UT真的很实用,价值也很高,但是很遗憾UT被大部分开发所忽略。当然本人对UT的理解、实践还不够,仍需继续实践模式。
最后说一句:当开发完功能,跑完UT,你可以放心的上线了的时候,你的UT就成功了。
AnotherRedisDesktopManager 开始收费了?
Spring Boot 3.0 M1 发布,正式弃用 Java 8
关注Java技术栈看更多干货
获取 Spring Boot 实战笔记!