號(hào)外苛秕!號(hào)外肌访!號(hào)外!你的 spring boot integration tests
運(yùn)行慢嗎艇劫,是不是每跑一次測(cè)試吼驶,你都在等待,等待它全綠的那一瞬間店煞。如果你遇到蟹演,那請(qǐng)接著往下看,也許可以幫助到你顷蟀。如果你沒(méi)有遇到酒请,那也請(qǐng)往下看,因?yàn)橐苍S以后你會(huì)遇到鸣个。
告訴你一個(gè)秘密:@MockBean
會(huì)導(dǎo)致測(cè)試類(Test Class)之間spring boot application context
不斷啟動(dòng)多次P叻础!囤萤!
不信昼窗,那么我們請(qǐng)看栗子 MockBean Annotation:
這項(xiàng)目有兩個(gè)測(cè)試類,AboutControllerTest 和 TokenControllerTest
AccountControllerTest:
account-controller.png
TokenControllerTest:
token-controller.png
AccountControllerTest
沒(méi)有使用 @MockBean
阁将,TokenControllerTest
使用 @MockBean
膏秫。下面是兩個(gè)測(cè)試一起運(yùn)行產(chǎn)生的日志:
spring-boot-log.png
如上圖所示右遭,spring boot
啟動(dòng)兩次做盅。而spring boot
的啟動(dòng)時(shí)間也比較耗時(shí)缤削,所以@MockBean
,很有可能導(dǎo)致測(cè)試運(yùn)行的很慢吹榴。那@MockBean
到底是個(gè)怎么樣的存在亭敢?
WHAT
寫(xiě)過(guò)spring boot integration test
的小伙伴,對(duì)于@MockBean
應(yīng)該會(huì)比較熟悉图筹。在寫(xiě)測(cè)試時(shí)帅刀,對(duì)于一些應(yīng)用的外部依賴需要進(jìn)行一些Mock
處理,比如:Redis
远剩、ElasticSearch
扣溺、ExternalService
等。官方文檔介紹 Mocking and spying beans:
mock-bean.png
- 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.
也就是說(shuō)瓜晤,@MockBean
會(huì)改變spring boot application context beans
锥余,導(dǎo)致使用了@MockBean
的測(cè)試類之間的需要不同application context
,從而導(dǎo)致spring boot application context
重啟痢掠。為什么需要不同application context
就需要重啟驱犹??足画?帶著疑惑雄驹,我們接著往下看。
WHY
Spring Boot Application Context
什么是application context
淹辞?簡(jiǎn)單理解医舆,就是應(yīng)用程序運(yùn)行所需要的上下文。官方文檔介紹 Context Management:
context-management.png
官方文檔介紹 Context management and caching:
testing-ctx-management.png
根據(jù)官方文檔意思象缀,application context
為初始化測(cè)試實(shí)例提供上下文彬向,如果需要不同的application context
實(shí)例化不同的測(cè)試,就需要重新啟動(dòng)spring boot
攻冷,創(chuàng)建不同applicaiton context
娃胆。文檔還說(shuō)到,為了解決spring boot application context
啟動(dòng)慢的問(wèn)題等曼,會(huì)做緩存處理里烦。那@MockBean
到底破壞了什么樣的緩存規(guī)則,從而導(dǎo)致spring boot
重啟多次禁谦?是什么導(dǎo)致打開(kāi)方式出了問(wèn)題胁黑?
Spring Boot Application Context Cache
要回答這個(gè)問(wèn)題,就要先了解[application context caching]的uniquely key
包含的內(nèi)容州泊,附上官方文檔介紹 Context caching:
context-caching.png
根據(jù)文檔的描述丧蘸,不難知道application context cacheing
是通過(guò)key:value
方式進(jìn)行緩存的,唯一鍵為組合鍵遥皂,包含:locations力喷、classes刽漂、contextInitializerClasses、contextCustomizers弟孟、contextLoader贝咙、parent、activeProfiles拂募、propertySourceLocations庭猩、propertySourceProperties、resourceBasePath
陈症。
而@MockBean
的使用會(huì)導(dǎo)致每個(gè)application context
中contextCustomizer
的不同蔼水,從而導(dǎo)致存儲(chǔ)在context cache
中的application context
的uniquely key
不同,最終導(dǎo)致application context
在測(cè)試類之間不能共享录肯。雖然沒(méi)有官方文檔說(shuō)明這一點(diǎn)徙缴,不過(guò)在
org.springframework.boot.test.mock.mockito.MockitoContextCustomizerFactory 源代碼中可以找到一些痕跡:
mock-customizer.png
圖中所說(shuō)的MergedContextConfiguration
就是application context caching
的uniquely key
。
HOW
對(duì)于spring boot integration test
來(lái)說(shuō)嘁信,除了 external service(clients...)
需要被 Mock
于样,其它的內(nèi)部依賴(service、repository…)都不應(yīng)該被Mock
潘靖。external service
可以在配置層穿剖,進(jìn)行Mock
,然后在測(cè)試類中卦溢,直接通過(guò)@Auotwrite
方式注入:
RedisTemplateBeanConfigurationMocker:
redis-configuration-mocker.png
redis-configuration.png
token-controller-fixed.png
TokenControllerTest
糊余,直接 @Autowrite RedisTemplateBeanConfigurationMocker
中配置的,RedisTemplate @Bean
单寂。完成栗子贬芥,請(qǐng)查mockbean-annotation。
寫(xiě)在最后
spring boot integration test
相對(duì)于 api test
宣决,應(yīng)該更關(guān)注api
功能的完整性蘸劈,了解依賴的邊界,不需要Mock
的尊沸,就不要Mock
威沫,比如:service, repository…
。對(duì)于外部依賴洼专,統(tǒng)一在配置層完成 Mock
棒掠,比如:client、redis屁商、rabbitmq...
烟很。