最近公司要求项目在使用maven构建的时候不能跳过test的生命周期,也就是通过mvn test命令需要将整个项目运行起来。因为之前项目组的成员都是在eclipse中去执行的unit test,在maven对所有模块构建的都是直接-Dmaven.test.skip=true的方式直接跳过UT的。
这次刚好借这个机会把项目所有模块中的UT整理下,在整理的过程中遇到了很多的问题,这边我把几个比较严重的问题记录了下。
先介绍下公司使用的基本中间件:DUBBO,RocketMQ,Spring。
(一) 环境问题
1. 问题描述:由于项目中含有多个子模块,但是基础的环境比如DUBBO和RocketMQ,甚至基本的Spring启动环境都是相同的,但是目前的项目中并没有抽象出统一的启动环境,导致的问题就是每个新加入的团队成员做单元测试的时候都需要自己去了解每个中间件环境的细节,这些与业务无关的细节会严重拖累新加入成员的开发进度。最后为了赶进度,也为了减少学习的成本,后加入的成员,就直接把其他子工程的环境直接复制放入到自己的工程中来启动UT。最后导致的问题就是由于对细节的不了解,最后按了葫芦起了瓢,不仅没起到快速启动的效果,反而在解决各种启动报错的问题中耗费了时间。而由于是复制别人的环境,所以环境中启动了很多自己工程中在运行中其实不要启动的组建。最糟糕的就是复制了全环境,有时我们的单测其实只需要运行一个简单的service的某个方法的细节,最后导致启动了各种DUBBO服务,RocketMQ的服务和客户端,各种数据库连接池,扫描了所有的bean等等,最后的问题就是一个很简答的UT都启动的特别慢。
2. 解决思路:针对项目抽象出一个单独的工程,提供给业务模块需要的时候引入。此独立的工程只需要提供基本统一的环境就,并且对外提供抽象接口供业务测试单元继承。可以借助spring-test帮你构建。
(二) DAO层数据插入问题(不能二次执行)
1.问题描述:很多人在测试DAO层的数据插入时,简单的构造了条数据当时插入成功了,程序员觉得万事大吉了,但是他们忘了项目的UT是要经过一次次的执行的,这样当你下次重复执行的是不是就会发现数据库抛出duplicate primary key的错误。
2. 解决思路:关于此类问题的解决方法有很多,但是一般有个原则就是单测的数据不应该污染数据库,也就是不应该持久化到数据库中的,可以使用事务控制整个UT,并在测试方法结束的时候回滚,目前Spring已经提供了此类事务的支持(有兴趣的可以看下spring的@TransactionConfiguration和@Tranactional以及AbstractTransactionalJUnit4SpringContextTests抽象类);
就算不用事务支持的话,也应该在插入前对数据库进行一次删除操作,最起码不应该让UT二次执行的时候就会抛出错误,这是最基本的要求。
(三) 对外部依赖的问题
1. 问题描述:现在很多工程都是基于SOA的架构设计的,各业务模块是以服务的方式对外提供服务。这样在UT的时候基本很难避免对其他业务接口的调用。大家知道测试环境那个的服务都是不稳定的,也许在你们调试的时候可用,下次执行的时候对方测试的接口不可用了,如果是基于DUBBO的服务,那么你在执行单测的时候就得眼巴巴的等到对方超时返回,或者返回不符合预期,测试不通过。
2.解决思路:Mock 还是Mock,Mock框架的话我首选的还是Easy Mock。
(四) 数据过期超时导致的异常
1.问题描述:严格来说这个不算问题,但是如果你向我一样,要在后期去优化整个项目的UT工程的时候你就会崩溃,最简单的场景:你要查某个时间段的数据,而且期望的是数据的条数大于0,也许当时你能测试通过,但是这个业务场景可能是依赖于时间段的,我后面来执行的时候数据过了有效期,此时UT是不能被通过的。
2.解决思路:对可能出现数据超时的情况进行异常处理。
以上是我在整理和重构的时候遇到的几个比较频繁和严重的问题,所以就归纳出来了。
我觉得一个合格的单元测试最起码的要求是能重复执行,并且不应该对外部的执行时有强依赖,UT只是测试项目中最小的逻辑甚至是代码单元的,所以应该小而简单。希望以后大家在构建单测的时候能够尽量避免我上面列出的几个问题,养成良好的习惯,提高单测质量!
已有 0 人发表留言,猛击->> 这里<<-参与讨论
ITeye推荐