@MockBean 危害

号外!号外!号外!你的 spring boot integration tests 运行慢吗,是否是每跑一次测试,你都在等待,等待它全绿的那一瞬间。若是你遇到,那请接着往下看,也许能够帮助到你。若是你没有遇到,那也请往下看,由于也许之后你会遇到。html

告诉你一个秘密:@MockBean会致使测试类(Test Class)之间spring boot application context不断启动屡次!!!java

不信,那么咱们请看栗子 MockBean Annotation:git

这项目有两个测试类,AboutControllerTestTokenControllerTestgithub

AccountControllerTest:redis

AccountControllerTest

TokenControllerTest:spring

TokenControllerTest

AccountControllerTest 没有使用 @MockBeanTokenControllerTest 使用 @MockBean。下面是两个测试一块儿运行产生的日志:api

spring boot log

如上图所示,spring boot 启动两次。而spring boot 的启动时间也比较耗时,因此@MockBean,颇有可能致使测试运行的很慢。那@MockBean究竟是个怎么样的存在?缓存

WHAT

写过spring boot integration test的小伙伴,对于@MockBean应该会比较熟悉。在写测试时,对于一些应用的外部依赖须要进行一些Mock 处理,好比:RedisElasticSearchExternalService 等。官方文档介绍 Mocking and spying beansapp

mock bean

  • It allows to add Mockito mocks in a Spring ApplicationContext.
  • If a bean, compatible with the declared class exists in the context, it replaces it by the mock.
  • If it is not the case, it adds the mock in the context as a bean.

也就是说,@MockBean会改变spring boot application context beans,致使使用了@MockBean的测试类之间的须要不一样application context,从而致使spring boot application context重启。为何须要不一样application context 就须要重启???带着疑惑,咱们接着往下看。spring-boot

WHY

Spring Boot Application Context

什么是application context?简单理解,就是应用程序运行所须要的上下文。官方文档介绍 Context Management:

context management

官方文档介绍 Context management and caching

testing-ctx-management

根据官方文档意思,application context为初始化测试实例提供上下文,若是须要不一样的application context实例化不一样的测试,就须要从新启动spring boot,建立不一样applicaiton context。文档还说到,为了解决spring boot application context启动慢的问题,会作缓存处理。那@MockBean到底破坏了什么样的缓存规则,从而致使spring boot重启屡次?是什么致使打开方式出了问题?

Spring Boot Application Context Cache

要回答这个问题,就要先了解application context cachinguniquely key包含的内容,附上官方文档介绍 Context caching

Context Caching

根据文档的描述,不难知道application context cacheing是经过key:value方式进行缓存的,惟一键为组合键,包含:locations、classes、contextInitializerClasses、contextCustomizers、contextLoader、parent、activeProfiles、propertySourceLocations、propertySourceProperties、resourceBasePath

@MockBean的使用会致使每一个application contextcontextCustomizer的不一样,从而致使存储在context cache中的application contextuniquely key不一样,最终致使application context在测试类之间不能共享。虽然没有官方文档说明这一点,不过在
org.springframework.boot.test.mock.mockito.MockitoContextCustomizerFactory 源代码中能够找到一些痕迹:

MockitoContextCustomizerFactory

图中所说的MergedContextConfiguration就是application context cachinguniquely key

HOW

对于spring boot integration test 来讲,除了 external service(clients...) 须要被 Mock,其它的内部依赖(service、repository…)都不该该被Mockexternal service 能够在配置层,进行Mock,而后在测试类中,直接经过@Auotwrite方式注入:

RedisTemplateBeanConfigurationMocker

redis configuration mocker

RedisConfiguration

redis configuration

TokenControllerTest:

token controller fixed

TokenControllerTest,直接 @Autowrite RedisTemplateBeanConfigurationMocker 中配置的,RedisTemplate @Bean。完成栗子,请查mockbean-annotation

写在最后

spring boot integration test 相对于 api test,应该更关注api功能的完整性,了解依赖的边界,不须要Mock的,就不要Mock,好比:service, repository…。对于外部依赖,统一在配置层完成 Mock,好比:client、redis、rabbitmq...

原文连接
相关文章
相关标签/搜索