为什么需要写Java单元测试总结

目录

前言

一、为什么写单元测试

写单测好处

1、提升效率

2、场景覆盖全

单测怎么写

1、集成测试

2、单元测试

Mock框架

1、Mockito单元测试

[2、Mockito 中文文档地址](#2、Mockito 中文文档地址)

二、强制要求

1.好的单元测试必须遵守AIR原则。

2.单元测试应该是全自动执行的,并且非交互式的。

3.保持单元测试的独立性。

4.单元测试是可以重复执行的,不能受到外界环境的影响。

被测系统

5.对于单元测试,要保证测试粒度足够小。

6.核心业务、核心应用、核心模块的增量代码,确保单元测试通过。

7.单元测试代码目录

三、推荐要求

1.单元测试的基本目标

2.编写单元测试代码时遵守BCDE原则,以保证被测试模块的交付质量。

3.对于数据库的相关查询、更新和删除等操作

4.和数据库相关的单元测试

5.对于不可测的代码

6.在设计评审阶段

7.单元测试作为一种质量保障手段

四、参考要求

1.为了更方便地进行单元测试,业务代码应该避免一下情况

2.不要对单元测试存在如下误解

五、单元测试与集成测试的区别

1.测试对象不同

2.测试方法不同

3.测试时间不同

4.测试内容不同

六、为什么要使用Mock做测试

1.Mock可以用来解除外部服务依赖,从而保证了测试用例的独立性。

2.Mock可以减少全链路测试数据准备,从而提高了编写测试用例的速度。

3.Mock可以模拟一些非正常的流程,从而保证了测试用例的代码覆盖率。

4.Mock可以不用加载项目环境配置,从而保证了测试用例的执行速度。


前言

为了保证代码质量,在写完代码后,写单测是很有必要的。当然,在大部分情况下,我们可能不会写单测,而是直接把应用部署起来,直接自测,然后再联调。估计很大一部分人,都是用这种方式开发。当然,我之前也是按这个方式来开发,单测覆盖率纯粹是为了满足公司的指标要求,大部分流于形式。

一、为什么写单元测试

说到单元测试,就不得不提起另一个词,TDD(Test-Driven Development)测试驱动开发:在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码

测试驱动开发虽然饱受争议,不过有这种方法论的推出并有不少的同行在践行,起码能够说明测试的重要性

1、当我们想测试部分代码逻辑是否正常的时候,我们可能会直接psvm来构造数据进而调试。那如果有一种东西能把我们psvm统一放到某个地方呢?

2、当我们在一个系统里边修改了很多代码时,又不确定改动是否影响在核心逻辑时。那如果有一种东西能在编译的时候,顺便自动跑一遍逻辑做回归呢?无论是重构还是正式提测前,都提高了自己写代码的信心。

3、当我们很容易一不小心时就把代码写成一坨屎,那如果有一种东西能让我们在编码的时候就注重自己的代码设计呢?

4、当我们这个季度什么都没干,但是系统没发生过故障,那如果有一种东西能让我们在KPI上添上浓墨的一笔呢?

5、....欢迎补充

没错,这东西就是单元测试

写单测好处

慢慢的,我感受到写单测带来的几个好处:

1、提升效率

启动一个应用,几分钟,找bug,修bug,再重启应用,这个过程不断的重复,应用重启太浪费时间。

而单测不需要重启整个应用,只对几个service做测试,效率高很多。

2、场景覆盖全

单测可以对代码运行中的各种情况进行模拟,并对最终的返回结果断言,这是自己自测很难模拟的。而且,这些单测,是沉淀的资产。下次修改代码,可以重跑以前单测,发现问题,避免踩坑。

单测怎么写

我们很容易对单测产生误解,所以这里我先把2个概念说明一下:

1、集成测试

测试过程中,会启动整个Spring容器,调用DB 或者 依赖的外部接口等。只不过访问的环境是测试环境。这个过程最大程度还原生产环境过程,但是耗时长。

2、单元测试

不启动整个应用,只对单个接口/类进行测试。不调用DB 、外部接口,依赖的服务都Mock掉,只测试代码逻辑。这个过程,测试用例耗时短。

我们说的单测,是指第2种。

单测过程分2步,第一步:Mock外部依赖,第二步:断言

Mock框架
1、Mockito单元测试
java 复制代码
 <dependency>
     <groupId>org.mockito</groupId>
     <artifactId>mockito-core</artifactId>
     <version>3.3.3</version>
     <scope>test</scope>
 </dependency>
2、Mockito 中文文档地址

Mockito库能够Mock对象、验证结果以及打桩(stubbing)。

GitHub - hehonghui/mockito-doc-zh: Mockito框架中文文档

二、强制要求

1.好的单元测试必须遵守AIR原则
  • A:Automatic(自动化)

  • I:Independent(独立性)

  • R:Repeatable(可重复)

单元测试在线上运行时,感觉像空气(AIR)一样感觉不到,但在测试质量的保障上,却是非常关键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。

2.单元测试应该是全自动执行的,并且非交互式的

测试用例通常是被定期执行的 ,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试,不是一个好的单元测试。

单元测试中不准使用System.out 来进行人肉认证,必须使用assert来验证

3.保持单元测试的独立性

为了保证单元测试稳定可靠且便于维护,单元测试用例之间绝不能互相调用 ,也不能依赖执行的先后次序。

4.单元测试是可以重复执行的,不能受到外界环境的影响。

单元测试通常会被放到持续集成中 ,每次有代码check in 时单元测试都会被执行

如果单测对外部环境(网络、服务、中间件等)有依赖,容易导致持续继承机制的不可用。

正例:为了不受外界环境影响,要求设计代码时就把 SUT 的依赖改成注入,在测试时用 spring 这样的 DI框架注入一个本地(内存)实现或者 Mock 实现。

被测系统

被测系统(System under test, SUT)表示正在被测试的系统,目的是测试系统能否正确操作。

根据测试类型的不同, SUT 指代的内容也不同, 例如 SUT 可以是一个类甚至是一整个系统。

5.对于单元测试,要保证测试粒度足够小。

有助于精确定位问题 ,单测粒度至多是类级别,一般是方法级别。

6.核心业务、核心应用、核心模块的增量代码,确保单元测试通过。

说明:新增代码及时补充单元测试,如果新增代码影响了原有单元测试,请及时修正。

7.单元测试代码目录

必须写在如下工程目录:src/test/java,不允许写在业务代码目录下。

说明:源码编译时会跳过此目录,而单元测试框架默认是扫描此目录。

三、推荐要求

1.单元测试的基本目标

语句覆盖率达到70%,核心模块的语句覆盖率和分支覆盖率都要达到100%

说明:在DAO层,Manager层和可重用度高的Service中都应该进行单元测试。

分支覆盖率 :5个分支,那么对应的应该有10条语句(一个分支有两条语句,ture和false),如果你执行了其中的5条,那么覆盖率就是50%。

2.编写单元测试代码时遵守BCDE原则,以保证被测试模块的交付质量。
  • B:Border,边界值测试 ,包括循环边界、特殊取值、特殊时间点、数据顺序等。

  • C:Correct,正确的输入,并得到预期的结果。

  • D:Design,与设计文档相结合,来编写单元测试。

  • E:Error,强制错误信息输入,并得到预期的结果。

3.对于数据库的相关查询、更新和删除等操作

不能假设数据库里的数据是存在的,或者直接操作数据库将数据插进去,请使用程序插入或者导入数据的方式来准备数据。

4.和数据库相关的单元测试

可以设定自动回滚机制 ,不给数据库造成脏数据,或者对单元测试产生的数据有明确的前后缀标识。

正例:在企业智能事业部的内部单元测试中,使用 ENTERPRISE_INTELLIGENCE UNIT_TEST 的前缀来标识单元测试相关代码。

5.对于不可测的代码

适当的时机做必要的重构,使代码变得可测,避免为了达到测试要求而书写不规范测试代码。

6.在设计评审阶段

开发人员需要和测试人员一起确定单元测试范围,单元测试最好覆盖所有测试用例。

7.单元测试作为一种质量保障手段

项目提测前完成单元测试,不建议项目发布后补充单元测试用例。

四、参考要求

1.为了更方便地进行单元测试,业务代码应该避免一下情况
  • 构造方法中做的事情过多

  • 存在过多的全局变量和静态方法

  • 存在过多的外部依赖

  • 存在过多的条件语句

说明:多层条件语句建议使用卫语句、策略模式、状态模式等方式重构。

如果条件语句极其复杂,就应该将条件语句拆解开,然后逐个检查,并在条件为真时立刻从函数中返回,这样的单独检查通常被称之为"卫语句"(guard clauses)

摘自《重构---改善既有代码的设计》

卫语句的效果就是将原来需要仔细阅读代码、细心整理逻辑的条件判断整理成一眼能看透的逻辑关系,效果就像以下:

java 复制代码
if(obj != null){
   doSomething();
 }

转换成卫语句以后的代码如下:

java 复制代码
 if(obj == null){
   return;
 }
 ​
  doSomething();
2.不要对单元测试存在如下误解
  • 那是测试同学干的事情。单元测试也是和开发同学强相关的。

  • 单元测试代码是多余的。系统的整体功能和各单元部件的测试真长与否是强相关的。

  • 单元测试代码不需要维护。一年半载后单元测试几乎都会处于废弃朱状态。

  • 单元测试与线上故障没有辩证关系。好的单元测试能够最大限度地规避线上故障。

五、单元测试与集成测试的区别

在实际工作中,不少同学用集成测试代替了单元测试,或者认为集成测试就是单元测试。

单元测试与集成测试的区别:

1.测试对象不同

单元测试对象是实现了具体功能的程序单元 ,集成测试对象是概要设计规划中的模块及模块间的组合

2.测试方法不同

单元测试中的主要方法是基于代码的白盒测试 ,集成测试中主要使用基于功能的黑盒测试

3.测试时间不同

集成测试要晚于单元测试。

4.测试内容不同

单元测试主要是模块内程序的逻辑、功能、参数传递、变量引用、出错处理及需求和设计中具体要求方面的测试 ;而集成测试主要验证各个接口、接口之间的数据传递关系,及模块组合后能否达到预期效果。

六、为什么要使用Mock做测试

比如你现在想要测试一个方法是否是正常的,但是这个方法中有很多调用数据库的代码 ,那么我们就可以在每个调用数据库的地方打桩模拟一下访问完数据库之后的返回值,这样我们就可以在测试的时候避免访问数据库了,可以非常高效地完成我们的单元测试,已达到验证我们写的方法到底对不对的目的。

1.Mock可以用来解除外部服务依赖,从而保证了测试用例的独立性。

现在的互联网软件系统,通常采用了分布式部署的微服务,为了单元测试某一服务而准备其它服务,存在极大的依耐性和不可行性。

2.Mock可以减少全链路测试数据准备,从而提高了编写测试用例的速度。

传统的集成测试,需要准备全链路的测试数据,可能某些环节并不是你所熟悉的。最后,耗费了大量的时间和经历,并不一定得到你想要的结果。现在的单元测试,只需要模拟上游的输入数据,并验证给下游的输出数据,编写测试用例并进行测试的速度可以提高很多倍。

3.Mock可以模拟一些非正常的流程,从而保证了测试用例的代码覆盖率。

根据单元测试的BCDE原则,需要进行边界值测试(Border)和强制错误信息输入(Error),这样有助于覆盖整个代码逻辑。在实际系统中,很难去构造这些边界值,也能难去触发这些错误信息。而Mock从根本上解决了这个问题:想要什么样的边界值,只需要进行Mock;想要什么样的错误信息,也只需要进行Mock。

4.Mock可以不用加载项目环境配置,从而保证了测试用例的执行速度。

在进行集成测试时,我们需要加载项目的所有环境配置,启动项目依赖的所有服务接口。往往执行一个测试用例,需要几分钟乃至几十分钟。采用Mock实现的测试用例,不用加载项目环境配置,也不依赖其它服务接口,执行速度往往在几秒之内,大大地提高了单元测试的执行速度。

相关推荐
程序猿000001号14 小时前
探索Python的pytest库:简化单元测试的艺术
python·单元测试·pytest
星蓝_starblue2 天前
单元测试(C++)——gmock通用测试模版(个人总结)
c++·单元测试·log4j
whynogome2 天前
单元测试使用记录
单元测试
字节程序员2 天前
使用JUnit进行集成测试
jmeter·junit·单元测试·集成测试·压力测试
love静思冥想3 天前
Java 单元测试中 JSON 相关的测试案例
java·单元测试·json
乐闻x4 天前
如何使用 TypeScript 和 Jest 编写高质量单元测试
javascript·typescript·单元测试·jest
Cachel wood4 天前
Vue.js前端框架教程4:Vue响应式变量和指令(Directives)
前端·vue.js·windows·python·单元测试·django·前端框架
@TangXin5 天前
单元测试-Unittest框架实践
单元测试
十年一梦实验室5 天前
【C++】sophus : test_macros.hpp 用于单元测试的宏和辅助函数 (四)
开发语言·c++·单元测试
编码浪子5 天前
Springboot3.x配置类(Configuration)和单元测试
单元测试