专栏首页芋道源码1024小谈 Java 单元测试

小谈 Java 单元测试

摘要: 原文可阅读 http://www.iocoder.cn/Fight/A-little-bit-about-Java-unit-testing 「lepdou」欢迎转载,保留摘要,谢谢!

  • 什么是UT?
  • UT有什么价值?
  • Unit Test & Intergration Test
    • Local Integration Test
    • Remote Integration Test
    • Unit Test
  • case细到什么程度为好?
  • 总结

什么是UT?

UT(Unit Test)即单元测试

UT有什么价值?

大部分的开发都不喜欢写UT,原因无非以下几点:

  1. 产品经理天天催进度,哪有时间写UT
  2. UT是测试自己的代码,自测?那要QA何用?
  3. 自测能测出bug?都是基于自身思维,就像考试做完第一遍,第二遍检查一样,基本检查不出什么东西
  4. UT维护成本太高,投入产出比太低
  5. 不会写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操作的集成测试

  1. 调用C接口
  2. 调用R接口,验证C成功
  3. 调用U接口
  4. 调用R接口,验证U成功
  5. 调用D接口
  6. 调用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掉,就是单元测试了。因为我们只关心所测试的方法的业务逻辑,也就是真正高内聚的逻辑单元了。如下图:

(图四)

好处如下:

  1. 没有什么数据是造不出来的,通通返回Mock的对象
  2. 代码中的异常处理代码,也可以通过mock接口,使之抛出异常
  3. 不产生任何脏数据
  4. 跑case更快了,因为不用启动整个项目,相当于Main方法

有人会说,都mock了还测试个蛋蛋。

这就是对于单元测试的理解了,单元测试应该只针对于目标方法的业务逻辑测试,dao、其它service应该在它们自身的单元测试去测试。对于依赖的第三方,我们应该信任它们能正确的完成我们所预期的。这句话很难理解对不对?

举几个例子

例子一:方法的最后是执行dao的create操作,那么该如何验证?

我们应该验证的内容是:

  1. dao的create方法被调用了
  2. 调用次数是对的
  3. 调用参数也是对的

没错,只要这三个验证通过,那么这个case执行就是通过的。因为我们相信dao的create操作能正确的完成我们所预期的,只要我们调用了正确的次数并且参数都是对的。dao的执行的正确性保证是在该dao的单元测试做的。 在Remote Integration Test里面第三步验证道理是一样的,我们应该验证RPC接口被调用了且次数和参数都是对的,那么我们的case就算通过了,至于,RPC服务端是否正确执行是它们的事情不是我们所关心的。 Mockito框架的verify接口就是做这件事情的。如果你理解了上述内容,那么你就开窍了,UT不在变得这么难写。

什么时候用单元测试,什么时候用集成测试?

在本人的实践中摸索发现,对于简单的业务,比如crud型的瘦service,比较适合于集成测试。

以下情况适合于单元测试:

  1. Util类
  2. 含有远程调用的方法
  3. 输入少,业务逻辑复杂的方法
  4. 需要异常处理的方法

case细到什么程度为好?

这个问题也是比较经典的,一个方法要是所有的路径都覆盖到,那么要写很多的case,说真的累死人。我的建议是两个原则:

  1. 核心逻辑,容易出错的逻辑一定要覆盖到
  2. 根据自己的时间。 没必要写的非常多,毕竟case维护成本很高,业务逻辑一改,case得跟着改。

总结

本人目前在从事于开源项目(Apollo(配置中心))研发,开源项目对代码质量要求相对来说高一些,UT当然是很重要的一环。刚开始也不会写UT,当然态度上也不重视UT。老大的代码UT覆盖率很高,抱着对开源负责的态度慢慢接受学习UT,到后来尝了几次甜头后,发现UT真的很实用,价值也很高,但是很遗憾UT被大部分开发所忽略。当然本人对UT的理解、实践还不够,仍需继续实践模式。

最后说一句:当开发完功能,跑完UT,你可以放心的上线了的时候,你的UT就成功了。



本文分享自微信公众号 - 芋道源码(YunaiV)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-08-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【死磕Java并发】-----J.U.C之Condition

    此篇博客所有源码均来自JDK 1.8 在没有Lock之前,我们使用synchronized来控制同步,配合Object的wait()、notify()系列方法可...

    芋道源码
  • 【追光者系列】HikariCP 源码分析之 evict、时钟回拨、连接创建生命周期

    摘要: 原创出处 https://mp.weixin.qq.com/s/PjJVYkMY67i7T-93tPpK7g 「渣渣王子」欢迎转载,保留摘要,谢谢!

    芋道源码
  • RocketMQ 源码分析 —— Message 拉取与消费(下)

    本文主要讲解PushConsumer,部分讲解PullConsumer,跳过顺序消费。 本文主要讲解PushConsumer,部分讲解PullConsumer,...

    芋道源码
  • 音视频基础知识:流媒体核心技术梳理

    这个公众号已经发了十几篇文章,阅读量和订阅数也在稳定增长。为了后面大家交流更顺畅,大概知道每篇文章在音视频技术体系的位置。利用周末画了个脑图,梳理了下音视频核心...

    用户5796036
  • 即构音视频SDK:跨四平台、三种类型终端,让直播保持低延迟高画质

    说到音视频云服务,大多数人可能联想到的是网络直播应用场景,实际上,硬件对音视频云服务的需求也在逐渐提升。而这样的市场需求也推动了整个行业的发展,目前,阿里云、腾...

    BestSDK
  • 实时音视频互动解决方案实时音视频TRTC介绍课程

    因为新型肺炎疫情的原因,大家只能把所有线下的东西线上化,进行远程的协作、沟通、娱乐。音视频的信息熵最大,能够获取到的信息最多,音视频通信格外重要,沟通需要实时的...

    shixin
  • 存储器层次结构介绍

    这里先说一下存储器系统: 寄存器 -----> 高速缓存 -----> 主存储 存储器对程序的性能有着巨大的影响,程序的运行就是对数据的不停的计算和搬移,其中最...

    HeaiKun
  • 视频直播APP SDK选型

    即构科技由腾讯QQ团队创业,是市面暂时较好的推流SDK,但是费用太高,可以先做个对比。但美颜效果,连麦功能,狼人杀模式等确实相较其他SDK有很大的优势。

    走在河边的小鹿
  • 腾讯北大AVS产业联盟三方合作 共同制定互联网图像与视频标准

    3月18日,国际音视频产业论坛暨AVS标准十五周年年会在北京举行,大会上腾讯公司、北京大学数字视频编解码技术国家工程实验室、AVS产业联盟三方宣布达成合作,共同...

    腾讯音视频实验室
  • 腾讯音视频实验室:标准布局+技术创新,助力音视频体验深度优化

    腾讯科技讯 5月21日—23日,2019腾讯全球数字生态大会在昆明召开,来自腾讯音视频实验室的专家研究员商世东带来了题为《面向产业互联网的音视频技术发展展望》...

    腾讯音视频实验室

扫码关注云+社区

领取腾讯云代金券