@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...

原文連接
相關文章
相關標籤/搜索