博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
单元测试问题一览
阅读量:7050 次
发布时间:2019-06-28

本文共 4012 字,大约阅读时间需要 13 分钟。

    随着工程的越来越大,导致开发自测也越来越麻烦。工程大带来两大问题

  • 依赖越来越多,很多应用上下游关系链条特别长。只要有一个依赖在日常环境不具备,则日常不可用,只能发布到预发测试
  • 依赖多了后,每次启动都要加载不少东西。一次启动、发布少则几分钟,多的也要20来分钟,在预发测试非常耗时

曾经做过一段时间的商家群聊业务就是这个情况,感觉整个人效率下降了不少。那段时间开发需求,为了完成只能靠大量加班来完成。

一般使用的土办法是,对需要测试的类,注入的依赖bean注掉,依赖的数据也直接mock掉,或者直接写代码new出需要的依赖结果。这样测试功能与业务功能混在一下,有时可能忘记改回来。或者更加彻底的是把要测试的代码拿出来,测试完成再写回去,但不够便捷。

也有很多自动化测试框架、数据mock工具,但开发过程中追求的是速度与便捷。看了些都不太贴合开发人员开发过程中的自测。

其实需要解决的核心问题就是mock掉依赖、解决启动慢的问题,这样才能解放开发的效率。

一方面想让自己从这种境况中出来,一方面看是不是可以提高团队效率。于是构思写一个工具来解决掉这两个核心问题。

spring bean 生命周期大致分为实例化BeanFactoryPostProcessor、执行bean的构造器、为bean注入属性、执行初始化方法,其中有很多扩展点。

在实现BeanFactoryPostProcessor时注入进来的ConfigurableListableBeanFactory的类中,可对BeanDefinition进行操作,而BeanDefinition类正是解析xml中<bean ..../> 对应bean代码形式的定义。那么这个地方就可以实现对定义的bean增删改查(一下子变得low了),这对它操作也直接影响后后续的构造、初始化。

在执行bean的构造器的这一阶段,实现InstantiationAwareBeanPostProcessor的方法postProcessBeforeInstantiation返回的结果可替换spring new一个对象的操作。而这也让我们在传统spring提供的常规aop操作之外,提供了插入代理代码的一种路径。

基于以上两点,似乎就可以解决那两个问题。将多余的BeanDefinition删除掉,再用代理将需要mock的bean、方法替换掉。

以下是构思整体的一个流程图

  • 执行单元测试阶段

这个阶段实现了 ChironJunit4Runner 继承 Runner启动单元测试,以便分析测试单元的依赖,以及根据测试单元类注解加载mock数据。

分析的产出是得到单元直接依赖的bean(一级依赖:往往就是我们测试的bean),以及这些bean的依赖(二级依赖:往往就是需要mock的bean),现即假设一级依赖为需要测试的代码,二级依赖为需要mock的代码。

  • 操作bean的定义

在这一步实现BeanFactoryPostProcessor接口,利用注入进来的ConfigurableListableBeanFactory,将一级依赖、必要的bean(如spring自身的、影响启动的)以外的BeanDefinition。由于这一些是在生命周期比较靠前的阶段,可以排掉大部分bean的定义,直接省掉了他们的实例化、初始化时间。

  • 实例化bean/mock代码织入

继承InstantiationAwareBeanPostProcessorAdapter类重写postProcessBeforeInstantiation方法。这个只有一级依赖、必要的bean能到这,这部分直接使用spring自身的实例化。但这一步要分析出一级依赖的依赖即二级依赖,并将二级依赖的BeanDefinition再写入到spring容器中(已经过修改并非之前的定义)。对于新加的BeanDefinition,spring会自再次进行这一步。这时即可使用代理完全替换掉原来的类行为并织入mock代码。

至此理论上完成可行。

团队业务方向调整,新业务基本都是新起的工程对这个需求也没有那么急需。构思其实在之前项目时已经实现部分功能,但基于兴趣,还是在业余时间将他写完。

经过实践亲测构思可行,目前已经将这个测试组件写是工程代码里,计划下一步抽出成一个jar包,到时再给大家分享这个二方包的使用方法及一些心得体验。

这个自测小工具的核心是

  • 使用单元测试工具来快速测试业务逻辑,不需要频繁重启应用
  • 只加载你测试要用到的bean及其依赖树,不需要的一律不加载
  • 懒加载所有的bean

工具我已经打成jar包

com.taobao.brand.component
chiron
1.0.0-SNAPSHOT
test
复制代码

使用方法

  • 使用ChironSpringJunitRunner或者ChironJunit4Runner来启动单元测试

示例

@RunWith(PandoraBootRunner.class)@DelegateTo(ChironSpringJunitRunner.class)@ContextConfiguration({
"/module-test-context.xml"})public abstract class BaseSpringTest { public void setMtopLoginUserInfo(long userId,String nick,String deviceId) { MessageRequest request = new MessageRequest(); request.setUserId(userId); request.setNick(nick); request.setDeviceId(deviceId); MtopContext.putMessageRequest(request); }}复制代码

  • 配置spring加载工具

示例

classpath:/module-test-context.properties
com.taobao.hsf.app.spring.util.HSFSpringProviderBean
org.apache.ibatis.session.SqlSessionFactory
org.mybatis.spring.SqlSessionTemplate
javax.sql.DataSource
com.taobao.developtools
com.tmall.geotherm.dal.cache
com.tmall.geotherm.dal.common
com.tmall.geotherm.dal.dao
com.taobao.tair.impl.mc.MultiClusterTairManager
复制代码

其中LoadRequiredBeanSpringProcessor用来加载必须的bean及依赖树。配置needPackagePrefixList是配置必须加载的类的前缀(包括包路径),否则有的必须的类会被忽略掉。

LazyLoadBeanProcessor是用来懒加载所有bean。

  • mock示例

对于需要mock的服务(如远程方法等),可以通过@MockService跟@Service注解来实现,如果service注解指定名称,则的mock特定名称的服务,否则mock整个接口的所有实现的,如下图

优缺点

相比使用mock的方式,你依赖的BEAN都是真实的。不需要单独mock数据库,外围接口等麻烦的工作。但如果要mock,使用原有方案也不冲突。

但相对来说如果外围接口有问题,还是要自己mock的。

转载地址:http://prpol.baihongyu.com/

你可能感兴趣的文章
Python UDP broadcast PermissionError: [Errno 13] Permission denied
查看>>
错误处理的思考
查看>>
怀恋g9
查看>>
CF982C Cut 'em all! DFS 树 * 二十一
查看>>
python常见问题汇总
查看>>
含大量行的订单创建时候creditlimit校验最耗时间
查看>>
sql 条件in() 中值过多拆分
查看>>
51nod 1515:明辨是非 并查集合并
查看>>
html5图片懒加载
查看>>
首页大屏广告效果 jquery轮播图淡入淡出
查看>>
【转】深入理解JVM—JVM内存模型
查看>>
SSD CONTROLLER
查看>>
第二节课!
查看>>
REdis之maxmemory解读
查看>>
导入项目需要注意的问题
查看>>
Java 验证用户名、密码
查看>>
hdoj1010 奇偶剪枝+DFS
查看>>
doxygen
查看>>
iOS关于XML解析请求数据
查看>>
scrapy
查看>>