spring中使用Mockito解决Bean依赖树问题方法
前提
本文不是针对Mockito的入门教学,主要叙述如何简单的使用Mockito解决Bean依赖树问题,对于Mockito的学习请找其他的文章或者查阅官方文档
基本概念Junit初始化及存在的问题
spring应用在unittest时,test是独立运行的,所以需要自行initApplicationContext,启动Ioc容器。
Junit要求:Test类中涉及的所有Springbean注入成功才能完成applicationContext初始化,并启动IOC容器,否则无法执行unittest。
ApplicationContext初始化的两种方式手动注入(使用@Bean或者@Component注入所需的类)编写@Configuration类(使用@ComponentScan指定扫描beans)两种初始化方式存在的问题
方式一:
所需的beans中,一个bean少注入了就会导致无法初始化上下文需要注入的bean太多时,需要花费大量的时间和精力,排查缺漏难度大
方式二:
颗粒度难以把控,随着项目规模变大之后,可能导致bean导入过多,单元测试跑很久才能通过当项目规模大了之后,bean之间的依赖往往是复杂的,扫描bean的方式可能出现一些不属于自己模块的未知问题或者某些中间件在unitTest环境无法正常启动,导致无法初始化上下文什么是依赖树?
在开发应用时,往往会出现如上图的树型依赖,比如serviceA调用serviceB,serviceB又调用serviceC。
然而这只是一个简单的例子。真正的开发中,往往一个service会依赖多个service,以及多个dao,以此来实现业务逻辑。
而根据Junit要求,我们必须将树的路径经过的所有节点(bean)都注入才能完成spring上下文初始化。这时如果bean之间的依赖耦合过大时,就无法跳脱出两种初始化方式带来的问题。
什么是Mockito?
在测试过程中,对于某些不容易构造(如HttpServletRequest必须在Servlet容器中才能构造出来)或者不容易获取比较复杂的对象(如JDBC中的ResultSet对象),用一个虚拟对象(Mock对象)来创建以便测试的测试方法。
Mock最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。
简单来说:就是虚拟一个mock对象,这个对象在单元测试时会“狸猫换太子”,将原有bean进行替换,“骗过”spring初始化,成功启动ioc容器,以此规避常规初始化方式带来的种种问题。
开发场景
结合本人在工作中遇见的问题,当时我所写的模块进行unitTest时,就出现了依赖树过于庞大的问题。
首先,我采用了常规的手动注入(方式一),导致注入了很久都没注入完,无法执行测试。后来觉得这方法在这种情况不可行。然后,我采用了编写@Configuration类(方式二),同样也存在一些问题。一些不属于我负责模块的bean也被注入,其中某些涉及TaskSchedule的bean无法被正确注入,导致无法执行测试。此时一个个bean探索,解决问题显然不现实。最后,我采用Junit+Mockito结合的方式进行单元测试。按照依赖树大小进行区分。依赖树小的直接使用常规的手动注入(方式一),省事,同时保证大部分逻辑按照代码正常运行依赖树大的使用Mockito,避免前文提到的两种初始化方式导致的问题
使用1导入maven依赖
首先导入mockitomaven依赖,版本请根据自己的spring版本选择,否则会出现不兼容的情况。
org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine junit junit 4.12 test
注意:
此处导入了spring-boot-starter-test是因为这个依赖已经包含了mockito相关的jar包
spring-boot-starter-test可以使用@MockBean注解(mockito-core、mockito-all貌似不能)
@Mock和@MockBean的区别:
使用一个简单的Demo进行开发场景的模拟,采用Junit+Mockito结合的方式进行单元测试,根据依赖树大小区分出是否需要mock
如图,此处编写了一个ControllerA,ControllerA中依赖了2个bean:ServiceA,DaoA
分析过程:关于DaoA:由于Dao往往不会依赖其他的bean,所以此处可以使用常规的手动注入(方式一)即可。方便快捷关于ServiceA:由于serviceA依赖了serviceB(->DaoB)、serviceC(->DaoC),像这样的嵌套依赖的bean就可以使用Mockito,来解决依赖树问题3编写Test类
daoA使用@Bean注解注入即可
@Bean publicDaoAdaoA(){ returnnewDaoAImpl(); }
1.serviceA首先使用@MockBean注解,将serviceA模拟为MockBean,它将在spring上下文初始化时就替换掉原有Bean
@MockBean privateServiceAserviceA;
2.在test类执行前(@Before),使用MockitoAPI设置调用某个方法的返回值(你预期得到的返回结果),在Test类中调用这个方法时就会返回所指定的值
@Before publicvoidinit(){ MockitoAnnotations.initMocks(this);//只使用@MockBean时可省略这句 when(controllerA.serviceA_method()).thenReturn("666"); }
3.使用@InjectMocks通知依赖了serviceA的controllerA,在spring启动时,对controllerA这个bean进行相应的后置处理
@Autowired @InjectMocks privateControllerAcontroller;
4.单元测试时,就不会使用原有Bean的方法,而是使用MockBean及其已经指定了返回值的方法
@Test publicvoidtestDeepMock(){ Strings=controllerA.serviceA_method(); System.out.println(s); }
5.unitTest结果
以上就是本次介绍的全部相关知识点,感谢大家的学习和对毛票票的支持。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。