Context 快取

一旦 TestContext 框架為測試載入 ApplicationContext (或 WebApplicationContext),該 Context 就會被快取並重複用於在同一個測試套件中宣告相同唯一 Context 組態的所有後續測試。為了理解快取如何運作,重要的是要理解「唯一」和「測試套件」的含義。

ApplicationContext 可以通過用於載入它的組態參數組合來唯一識別。因此,唯一的組態參數組合用於產生一個金鑰,Context 在該金鑰下被快取。TestContext 框架使用以下組態參數來建立 Context 快取金鑰

  • locations (來自 @ContextConfiguration)

  • classes (來自 @ContextConfiguration)

  • contextInitializerClasses (來自 @ContextConfiguration)

  • contextCustomizers (來自 ContextCustomizerFactory) – 這包括 @DynamicPropertySource 方法以及 Spring Boot 測試支援的各種功能,例如 @MockBean@SpyBean

  • contextLoader (來自 @ContextConfiguration)

  • parent (來自 @ContextHierarchy)

  • activeProfiles (來自 @ActiveProfiles)

  • propertySourceDescriptors (來自 @TestPropertySource)

  • propertySourceProperties (來自 @TestPropertySource)

  • resourceBasePath (來自 @WebAppConfiguration)

例如,如果 TestClassA@ContextConfigurationlocations (或 value) 屬性指定 {"app-config.xml", "test-config.xml"},則 TestContext 框架會載入對應的 ApplicationContext,並將其儲存在 static Context 快取中,金鑰僅基於這些位置。因此,如果 TestClassB 也為其位置定義 {"app-config.xml", "test-config.xml"}(顯式或隱式透過繼承),但不定義 @WebAppConfiguration、不同的 ContextLoader、不同的活動 Profile、不同的 Context Initializer、不同的測試屬性來源或不同的父 Context,則相同的 ApplicationContext 會由兩個測試類別共用。這表示載入應用程式 Context 的設定成本僅發生一次(每個測試套件),並且後續的測試執行速度會快得多。

測試套件和 Fork 程序

Spring TestContext 框架將應用程式 Context 儲存在靜態快取中。這表示 Context 實際上儲存在 static 變數中。換句話說,如果測試在不同的程序中執行,則靜態快取會在每次測試執行之間清除,這實際上會停用快取機制。

為了從快取機制中受益,所有測試都必須在同一個程序或測試套件中執行。這可以通過在 IDE 中將所有測試作為一個群組執行來實現。同樣地,當使用建置框架(例如 Ant、Maven 或 Gradle)執行測試時,重要的是要確保建置框架不會在測試之間 Fork。例如,如果 Maven Surefire 外掛程式的 forkMode 設定為 alwayspertest,則 TestContext 框架無法在測試類別之間快取應用程式 Context,並且建置過程會因此而執行得慢得多。

Context 快取的大小有上限,預設最大大小為 32。每當達到最大大小時,就會使用最近最少使用 (LRU) 的驅逐策略來驅逐和關閉過時的 Context。您可以通過設定名為 spring.test.context.cache.maxSize 的 JVM 系統屬性,從命令列或建置 Script 組態最大大小。作為替代方案,您可以通過 SpringProperties 機制設定相同的屬性。

由於在給定的測試套件中載入大量應用程式 Context 可能會導致套件花費不必要的時間來執行,因此通常最好確切地知道已載入和快取了多少 Context。要檢視底層 Context 快取的統計資訊,您可以將 org.springframework.test.context.cache 記錄類別的日誌級別設定為 DEBUG

在不太可能發生的情況下,測試會損壞應用程式 Context 並需要重新載入(例如,通過修改 Bean 定義或應用程式物件的狀態),您可以使用 @DirtiesContext 註解您的測試類別或測試方法(請參閱 Spring 測試註解 中關於 @DirtiesContext 的討論)。這會指示 Spring 從快取中移除 Context,並在執行下一個需要相同應用程式 Context 的測試之前重建應用程式 Context。請注意,對 @DirtiesContext 註解的支援由 DirtiesContextBeforeModesTestExecutionListenerDirtiesContextTestExecutionListener 提供,預設情況下已啟用。

ApplicationContext 生命周期和控制台記錄

當您需要除錯使用 Spring TestContext 框架執行的測試時,分析控制台輸出(即,輸出到 SYSOUTSYSERR 串流)可能很有用。某些建置工具和 IDE 能夠將控制台輸出與給定的測試相關聯;但是,某些控制台輸出無法輕易地與給定的測試相關聯。

關於 Spring 框架本身或在 ApplicationContext 中註冊的組件觸發的控制台記錄,重要的是要了解由 Spring TestContext 框架在測試套件中載入的 ApplicationContext 的生命週期。

測試的 ApplicationContext 通常在準備測試類別的實例時載入,例如,對測試實例的 @Autowired 欄位執行依賴注入。這表示在 ApplicationContext 初始化期間觸發的任何控制台記錄通常無法與單個測試方法相關聯。但是,如果 Context 根據 @DirtiesContext 語意在測試方法執行之前立即關閉,則 Context 的新實例將在測試方法執行之前立即載入。在後一種情況下,IDE 或建置工具可能會將控制台記錄與單個測試方法相關聯。

測試的 ApplicationContext 可以通過以下其中一種情境關閉。

  • Context 根據 @DirtiesContext 語意關閉。

  • Context 已關閉,因為它已根據 LRU 驅逐策略自動從快取中驅逐。

  • 當測試套件的 JVM 終止時,Context 通過 JVM 關閉 Hook 關閉。

如果 Context 在特定測試方法之後根據 @DirtiesContext 語意關閉,則 IDE 或建置工具可能會將控制台記錄與單個測試方法相關聯。如果 Context 在測試類別之後根據 @DirtiesContext 語意關閉,則在 ApplicationContext 關閉期間觸發的任何控制台記錄都無法與單個測試方法相關聯。同樣地,通過 JVM 關閉 Hook 在關閉階段觸發的任何控制台記錄都無法與單個測試方法相關聯。

當 Spring ApplicationContext 通過 JVM 關閉 Hook 關閉時,在關閉階段執行的回呼會在名為 SpringContextShutdownHook 的執行緒上執行。因此,如果您希望停用在 ApplicationContext 通過 JVM 關閉 Hook 關閉時觸發的控制台記錄,您或許可以使用您的記錄框架註冊一個自訂篩選器,以允許您忽略由該執行緒啟動的任何記錄。