一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - springboot集成測試容器重啟問題的處理

springboot集成測試容器重啟問題的處理

2022-03-10 00:48micro_hz Java教程

這篇文章主要介紹了springboot集成測試容器重啟問題的處理,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

背景

spring boot test的項目中常用的測試框架, 最近在寫集成測試的時候發現一個比較奇怪的問題,當我在運行多個測試用例的時候會偶爾重新啟動整個容器上下文,由于后期業務逐漸復雜,大量的測試用例需要運行,這個問題直接導致回歸測試的效率降低。

springboot集成測試容器重啟問題的處理

舉個例子:

springboot集成測試容器重啟問題的處理

幾個類:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class BaseApiTest {
  @Test
  public void init() {
  }
}
public class ApiTest1 extends BaseApiTest {
  @MockBean
  private Service service;
  @Test
  public void test1() {
      service.call();
  }
}
public class ApiTest2 extends BaseApiTest {
  @Autowired
  private Service service;
  @Test
  public void test2() {
      service.call();
  }
}
@SpringBootApplication
@Slf4j
public class TestApplication {
  public static void main(String[] args) {
      log.info("啟動容器");
      new SpringApplication(TestApplication.class).run(args);
  }
}
@Component
public class Service {
  public void call() {
      System.out.println("service called");
  }
}

運行test包下所有測試:

springboot集成測試容器重啟問題的處理

發現容器重復啟動了。

 

測試用例的運行流程

可以開啟idea的線程堆棧跟蹤,觀察整個容器的啟動路徑

springboot集成測試容器重啟問題的處理

com.intellij.rt.junit是idea內部的實現,點擊idea的運行單測會觸發JunitStarter的main函數去啟動,可以去GitHub找到源碼:

springboot集成測試容器重啟問題的處理

做一些準備工作找到指定的runner就開始調用junit的包去執行編寫的單測,junit為了靈活的擴展不同的測試運行環境,類似SPI機制動態獲取Runner去運行單測。例如我的例子里指定了SpringRunner就是需要依賴Spring容器的一個實現,這樣就讓測試用例可以運行在Spring環境中。

springboot集成測試容器重啟問題的處理

junit的入口也支持在測例前后去插入一些操作,自己去實現RunnerListener即可。junit默認實現了監聽器去記錄測例的耗時,失敗的數量等信息。

springboot集成測試容器重啟問題的處理

我指定的Runner是SpringRunner,其與SpringJUnit4ClassRunner并沒什么區別,可以看其實現完全繼承了SpringJUnit4ClassRunner的實現。

所以我們直接看SpringJUnit4ClassRunner的runner實現:

springboot集成測試容器重啟問題的處理

它首先判斷了當前的環境是否需要忽略單測。如果忽略會在通知里得到通知。關于環境的指定控制可以參考注解@IfProfileValue,判斷環境正確之后繼續調用junit包父類ParentRunner的方法執行。

其定義了執行的基本的模板:

springboot集成測試容器重啟問題的處理

classBlock里面定義線程池執行和測例執行的一些before和after邏輯,里面的runChild是抽象方法,也是留給各個Runner實現的鉤子。getFilteredChildren能夠根據@Test注解拿到所有需要運行的用例方法,然后每個方法去調用具體的Runner運行。

springboot集成測試容器重啟問題的處理

其流程圖如下

springboot集成測試容器重啟問題的處理

springboot集成測試容器重啟問題的處理

SpringJUnit4ClassRunner的運行每個方法會給每個測例方法進行一個封裝成Statement。關鍵就在methodBlock方法,它實現了Spring boot對方法運行的封裝

springboot集成測試容器重啟問題的處理

createTest會在測試的上下文里維護一個配置,然后會用通知機制一樣去依次調用需要準備的東西,其中就包含spring容器的上下文。

springboot集成測試容器重啟問題的處理

springboot集成測試容器重啟問題的處理

其會執行injectDependencies,處理依賴的bean準備。TestContext是每個單測方法需要運行的上下文,在Spring boot的測試環境下,其維護了Spring的上下文,每個方法的執行都會去獲取Spring的上下文

springboot集成測試容器重啟問題的處理

根據單測的相關信息文獲取上spring的上下文,為了避免每次都去加載容器,TestContext會維護一個spring容器的緩存,CacheAwareContextLoaderDelegate

springboot集成測試容器重啟問題的處理

springboot集成測試容器重啟問題的處理

CacheAwareContextLoaderDelegate其內部的獲取又是通過單測配置信息去ContextCache獲取的,其內部是一個同步SynchronizedMap去保存的。

springboot集成測試容器重啟問題的處理

其內部實現看測例的配置信息去獲取加載過的容器,如果沒獲取到就會觸發重新加載新容器的流程,所以關鍵就是看key在Map中的獲取邏輯,其底層是spring test自己實現一個的Map

springboot集成測試容器重啟問題的處理

可以看到其是基于HashMap的一個哈希結構,根據Jdk的源碼,我們可以知道HashMap的key是根據hashCode與equals去比較key,那可以確定,要想復用同一個容器就得看Key值的hashCode和equals實現。接下來我們看MergedContextConfiguration源碼.

springboot集成測試容器重啟問題的處理

springboot集成測試容器重啟問題的處理

可以發現其比較的值是:

	 * @param testClass the test class for which the configuration was merged
	 * @param locations the merged context resource locations
	 * @param classes the merged annotated classes
	 * @param contextInitializerClasses the merged context initializer classes
	 * @param activeProfiles the merged active bean definition profiles
	 * @param propertySourceLocations the merged {@code PropertySource} locations
	 * @param propertySourceProperties the merged {@code PropertySource} properties
	 * @param contextCustomizers the context customizers
	 * @param contextLoader the resolved {@code ContextLoader}
	 * @param cacheAwareContextLoaderDelegate a cache-aware context loader
	 * delegate with which to retrieve the parent context
	 * @param parent the parent configuration or {@code null} if there is no parent

這些參數確定了能否共享SpringApplication,那兩個測試類一個@Autowired,另一個使用@MockBean,肯定是改變這里面某個值,我們可以回溯這個MergedContextConfiguration是在什么時候被初始化的。這個還要追溯到idea的啟動類,找到Runner的時候,SpringJUnit4ClassRunner的初始化的過程。

在每個測試類的運行都會喚起SpringJUnit4ClassRunner初始化,調用構造函數的時候會去加載測試類的上下文

springboot集成測試容器重啟問題的處理

去創建這個TextContextManager

springboot集成測試容器重啟問題的處理

這里首先會根據被測試類的繼承關系和注解的遞歸去找到固定包下面被注解@BootstrapWith修飾的類,因為是Spring boot test這里會根據@SpringBootTest 注解找到SpringBootTestContextBootstrapper類,找到這個引導類之后就會去初始化MergedContextConfiguration了。

springboot集成測試容器重啟問題的處理

引導類通過SPI機制加載到所有的Customizer,并根據需要DefinitionsParser,進行轉換,保存在MergedContextConfiguration的一個字段,mock的一個屬性會在轉換的時候記錄到,而非mock的contextCustomizers則不會記錄。

注意這里提到的

springboot集成測試容器重啟問題的處理

springboot集成測試容器重啟問題的處理

兩個類一個用mock的字段,一個用非mock的字段,兩個MockitoContextCustomizer的definitions就不一樣,因此無法共享上下文,因此需要重新啟動一個Spring容器,并存放到CacheAwareContextLoaderDelegate,以便后面共享。

 

結論

分析源碼的設計,發現應用了很多SPI與可擴展的設計,idea與junit的解耦,junit的抽象與模板定義與各個測試框架的擴展。

針對容器重啟的角度,對于一個類來說,一定是共享一個spring上下文,但是不同的類可能由于注入的bean的方式不同導致無法共享spring上下文,所以導致重啟會浪費掉一些時間,因此建議確定好mock的邊界,對盡量多的測例共享一個容器視角可以提高單測效率,基于此可以設計多繼承關系的單測結構,并把注入的bean向上共享,避免各個測試子類自己去注入出現不一致的情況。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/micro_hz/article/details/106280649

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美日韩国产一区二区三区欧 | 2019国内自拍大神视频 | 欧美草逼网站 | 王的视频vk| 国产精品欧美日韩一区二区 | 欧美日韩亚洲高清不卡一区二区三区 | 给我一个黄色网址 | 色老板免费 | 午夜精品久久久久久久99蜜桃i | 草草视频人人爽 | 亚洲精品老司机福利在线播放 | 午夜国产福利视频一区 | 无码人妻少妇色欲AV一区二区 | 91精品国产在线 | 精品区2区3区4区产品乱码9 | 欧美人鲁交大全 | 国产高清路线一路线二2022 | 欧美亚洲国产综合在线 | 红杏网 | 国产精品亚洲一区二区久久 | 亚洲一区二区三区91 | 好大~好爽~再进去一点 | 亚洲国产天堂久久精品网 | 色呦呦在线免费观看 | 亚洲高清免费在线观看 | 四虎视屏| 四虎永久免费地址在线网站 | 男人的天堂日本 | 日本欧美大码a在线视频播放 | 国产成人免费在线观看 | 色综合天天娱乐综合网 | 午夜在线观看免费完整直播网页 | 91午夜视频| 国产成人欧美视频在线 | 日本人添下面的全过程 | 爱操综合网 | 亚洲国产精品第一页 | 污污免费| 国产 日韩 一区 | 免费一级片在线 | 91短视频在线观看2019 |