1.后端为什么要单元测试
1.1 单元测试是考验系统稳定性一个重要方面
为什么这样讲,虽然你可以做到全流程的测试,一群人坐在一个小会议室,挨个点击功能,把流程走完,这确实是一种测试方式。但是这种方式只能
针对比较简单的功能,比如增删改查,不能解决复杂功能 , 比如定时器计算,奖励计算 ,返佣计算的场景,撮合引擎调度。上述谈到场景功能非常复杂,数据依赖性大,流程贯穿性强,逻辑要求性高,错误不容易发现。 同时一起点击情况,不能解决一些异常情况,异常数据的场景,比如边缘数据,不能针对并发场景,性能场景, 所以全流程测试只能测试功能,而不能测试到模块的分支。
而单元测试有助于函数级别,类级别的测试,他的关注点会更小,只有一个个函数级别功能没有问题,那么整个系统才没有问题。当然一个个函数没有问题,系统面临最大的问题就是联调的问题。
而系统一旦联调成功,很难出现问题。联调熟悉大逻辑问题,语义完整,一般不会出现问题。
1.2 方便进行回归测试
程序提测后,发布后,肯定有一些功能需要修改,那么这个时候让测试全部去点一遍,难度太大,测试这个时候也是走马观花的过一遍。这个时候如果能够合理布局单元测试,能够全部跑一下单元测试,那么bug 几率会大大降低,从而减少线上故障
1.3 减少错误修复的成本
减少错误修复的成本,如果一个错误能够再单元测试中解决,那么他花费的时间可能只有几个小时,如果错误被测试发现成本更大,会影响程序员的KPI , 如果被客户发现,甚至要回滚数据,那么后果难以想象,甚至直接影响到项目成功
1.3 单元测试可以提高程序的能力和系统架构能力
写得class 能不能单元测试是衡量代码好坏的唯一标准。可以这样说不能进行单元测试的后端系统是不可信赖的。能够方便测试的代码一定是短小,依赖少,输入参数清晰,输出参数明了的的代码。很难想象一个超大的函数,甚至依赖到hadoop
这样的代码怎么进行单元测试,并且当后面进行代码重构的,单元测试可以方便进行重构,单元测试是理解代码的一部分。
2. java 后端单元测试遇到困难
2.1 系统启动太慢
当一个微服务的集成有liqubase , mybatis plus , dubbo 的时候,他启动时间在1分钟左右,启动完了在加载test 类 会更慢。真正执行test 的时间可能就是只有几秒钟,大部分时间都用来启动了
2.2 依赖数据库,依赖初始化,导致程序没有办法去进行单元测试
想象这样一个功能 , 读取数据库数据 , 处理数据 ,再写回数据库 ,写回的时候,还要判断有没有这个数据,如果存在这样的数据,就不写回这个数据(避免订单重复提交), 然后再读取数据库数据,进行其他的场景计算。整个程序面对就是数据库编程,或者是对外部接口的编程。这样一个比较复杂的场景,怎么进行单元测试 , 这种只能集成测试。所以代码的核心逻辑应该是面下内存编程,面向本地代码,必须把数据库的输入 输出挡在外面。
2.3 单元测试力度怎么划分
是以类为维度还是以函数为维度,本来而言,SOA 中面向的是接口的.类的主导地位很弱,有因为spring boot 线程驱动的关系,导致@service 会形成单例,不能够拥有私有变量。 所有封装性大大降低 以接口形式存在的类 在做单元测试的时候,实际上并不具备
类的相关封装特性,那么这个时候可以以Impl的实现类作为单元测试划分力度。举例说明, 你有一个接口叫做test , 有个接口的实现 testImpl ,那么你可以直接在单元测试时候注入 testImpl , 测试testImpl 底层接口
2.4 单元测试判断比较差 需要程序判断和人工判断相互结合的方式
很多时候人工进行判断,或者说一般认为没有报异常就表示OK 了 ,甚至一些操作接口返回直接是void ,为什么,因为一方面void 的接口可以把数据输出到 redis mysql , 所以他不需要再返回给外面。但是你做单元测试的时候void 又不能方便进行判断,导致
但单元测试依赖人去读日志,不能依赖于比较
2.5 本来编写代码的时间少,还做单元测试,是不是浪费时间 ,甚至需求都没有办法理解清楚
3. 解决单元测试的困难
(1)系统启动太慢的原因是因为springboot 启动,实际上我们可以把逻辑和依赖分离开来,我们针对数据处理部分,输入的入参,出参,在service 中形成独立的函数,这样我们就可以构造数据,判断数据的逻辑,并且逻辑的处理是重点。
那么这个地方就会造成数据来源参数过多的问题,比如你的数据来源是数据库 ,外部参数, 缓存判断,你的输出是争取是否是写数据。把准备数据的工作都进行抽象。
(2)针对系统启动太慢的
可以采取这样的方式:@RunWith(SpringRunner.class) @SpringBootTest @ComponentScan(basePackages = {"com.XX.XX.service"}) 这种方式,这种方式只加载系统扫描的类,但是还是会启动springboot 进行加载,最起码定时器不会启动。
(3)系统依赖数据库进行编程,这里一定要改变数据库编程的习惯,采用脱离数据库编程的方式,为什么
我认为数据库面向数据库编程会遇到两个问题
(4) 因为数据库编程面临事务隔离方面的问题,究竟什么时候可见。 总是操作数据库就会遇到这种问题,并且数据库隔离级别会导致你在可以重复读隔离级别上面没有问题的代码,如果在读未提交的级别上面就会有问题。
(5) 循环去读数据库或者循环去写数据库的网络开销和连接开销,数据库一次交互的网络开销 连接开销
(6) 但是这里要注意一点是事务控制,一定要再代理最上面进行事务控制。
(7) 单元测试代码的组织,按照原来java 的类的框架进行组织,原来框架是什么样子组织,后面就是什么样子组织,这种组织更加清晰,采用函数的方式进行组织过于零散。
包的组织要能够最快找到为目的,每次最快找到你要修改的类的大致范围
(8)尽量不要人工去判断代码,要用代码去判断代码,所以尽量不要用void 作为返回值,函数要有返回值,如果你update 返回数量 如果insert 返回主键
(9)单元测试不会浪费时间,只有单元测试做得好,才能说明充分理解需求,充分实现代码