使用測試屬性來源的內容組態

Spring Framework 對於具有屬性來源階層的環境概念提供一流的支援,您可以設定整合測試與測試專用的屬性來源。相較於 @Configuration 類別上使用的 @PropertySource 註解,您可以在測試類別上宣告 @TestPropertySource 註解,以宣告測試屬性檔案或內嵌屬性的資源位置。這些測試屬性來源會新增至 EnvironmentPropertySources 的集合,用於為已註解的整合測試載入的 ApplicationContext

您可以將 @TestPropertySourceSmartContextLoader SPI 的任何實作搭配使用,但 @TestPropertySource 不支援舊版 ContextLoader SPI 的實作。

SmartContextLoader 的實作透過 MergedContextConfiguration 中的 getPropertySourceDescriptors()getPropertySourceProperties() 方法,取得合併的測試屬性來源值。

宣告測試屬性來源

您可以使用 @TestPropertySourcelocationsvalue 屬性來設定測試屬性檔案。

依預設,同時支援傳統和基於 XML 的 java.util.Properties 檔案格式,例如 "classpath:/com/example/test.properties""file:///path/to/file.xml"。從 Spring Framework 6.1 開始,您可以透過 @TestPropertySource 中的 factory 屬性設定自訂 PropertySourceFactory,以支援不同的檔案格式,例如 JSON、YAML 等。

每個路徑都會解譯為 Spring Resource。純路徑 (例如,"test.properties") 會視為相對於定義測試類別的套件的類別路徑資源。以斜線開頭的路徑會視為絕對類別路徑資源 (例如:"/org/example/test.xml")。參考 URL 的路徑 (例如,以 classpath:file:http: 為前置字串的路徑) 會使用指定的資源協定載入。

路徑中的屬性預留位置 (例如 ${…​}) 將會針對 Environment 解析。

從 Spring Framework 6.1 開始,也支援資源位置模式,例如 "classpath*:/config/*.properties"

以下範例使用測試屬性檔案

  • Java

  • Kotlin

@ContextConfiguration
@TestPropertySource("/test.properties") (1)
class MyIntegrationTests {
	// class body...
}
1 使用絕對路徑指定屬性檔案。
@ContextConfiguration
@TestPropertySource("/test.properties") (1)
class MyIntegrationTests {
	// class body...
}
1 使用絕對路徑指定屬性檔案。

您可以使用 @TestPropertySourceproperties 屬性,以金鑰值組的形式設定內嵌屬性,如下一個範例所示。所有金鑰值組都會以單一測試 PropertySource 新增至封閉的 Environment,並具有最高優先順序。

金鑰值組的支援語法與 Java 屬性檔案中項目的定義語法相同

  • key=value

  • key:value

  • key value

雖然可以使用上述任何語法變體以及金鑰與值之間的任何空格數來定義屬性,但建議您在測試套件中使用一種語法變體和一致的間距,例如,考慮始終使用 key = value 而不是 key= valuekey=value 等。同樣地,如果您使用文字區塊定義內嵌屬性,則應在整個測試套件中一致地使用文字區塊來處理內嵌屬性。

原因是您提供的確切字串將用於判斷內容快取的金鑰。因此,若要從內容快取獲益,您必須確保一致地定義內嵌屬性。

以下範例設定兩個內嵌屬性

  • Java

  • Kotlin

@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port = 4242"}) (1)
class MyIntegrationTests {
	// class body...
}
1 透過字串陣列設定兩個屬性。
@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port = 4242"]) (1)
class MyIntegrationTests {
	// class body...
}
1 透過字串陣列設定兩個屬性。

從 Spring Framework 6.1 開始,您可以使用文字區塊在單一 String 中定義多個內嵌屬性。以下範例使用文字區塊設定兩個內嵌屬性

  • Java

  • Kotlin

@ContextConfiguration
@TestPropertySource(properties = """
	timezone = GMT
	port = 4242
	""") (1)
class MyIntegrationTests {
	// class body...
}
1 透過文字區塊設定兩個屬性。
@ContextConfiguration
@TestPropertySource(properties = ["""
	timezone = GMT
	port = 4242
	"""]) (1)
class MyIntegrationTests {
	// class body...
}
1 透過文字區塊設定兩個屬性。

@TestPropertySource 可以作為可重複註解使用。

這表示您可以在單一測試類別上有多個 @TestPropertySource 宣告,後續 @TestPropertySource 註解中的 locationsproperties 會覆寫先前 @TestPropertySource 註解中的項目。

此外,您可以在測試類別上宣告多個組合註解,每個註解都使用 @TestPropertySource 進行 meta-annotation,而所有這些 @TestPropertySource 宣告都會貢獻您的測試屬性來源。

直接呈現的 @TestPropertySource 註解始終優先於 meta-present @TestPropertySource 註解。換句話說,直接呈現的 @TestPropertySource 註解中的 locationsproperties 將會覆寫用作 meta-annotation 的 @TestPropertySource 註解中的 locationsproperties

預設屬性檔案偵測

如果 @TestPropertySource 宣告為空註解 (也就是說,沒有 locationsproperties 屬性的明確值),則會嘗試偵測相對於宣告註解之類別的預設屬性檔案。例如,如果已註解的測試類別為 com.example.MyTest,則對應的預設屬性檔案為 classpath:com/example/MyTest.properties。如果無法偵測到預設值,則會擲回 IllegalStateException

優先順序

測試屬性的優先順序高於作業系統環境、Java 系統屬性或應用程式透過使用 @PropertySource 以宣告方式或程式化方式新增的屬性來源中定義的屬性。因此,測試屬性可以用於選擇性地覆寫從系統和應用程式屬性來源載入的屬性。此外,內嵌屬性的優先順序高於從資源位置載入的屬性。但是請注意,透過 @DynamicPropertySource 註冊的屬性的優先順序高於透過 @TestPropertySource 載入的屬性。

在下一個範例中,timezoneport 屬性以及 "/test.properties" 中定義的任何屬性都會覆寫系統和應用程式屬性來源中定義的任何同名屬性。此外,如果 "/test.properties" 檔案定義了 timezoneport 屬性的項目,則這些項目會被使用 properties 屬性宣告的內嵌屬性覆寫。以下範例示範如何在檔案和內嵌中指定屬性

  • Java

  • Kotlin

@ContextConfiguration
@TestPropertySource(
	locations = "/test.properties",
	properties = {"timezone = GMT", "port = 4242"}
)
class MyIntegrationTests {
	// class body...
}
@ContextConfiguration
@TestPropertySource("/test.properties",
		properties = ["timezone = GMT", "port = 4242"]
)
class MyIntegrationTests {
	// class body...
}

繼承與覆寫測試屬性來源

@TestPropertySource 支援布林值 inheritLocationsinheritProperties 屬性,用於表示是否應繼承超類別宣告的屬性檔案和內嵌屬性的資源位置。這兩個旗標的預設值都是 true。這表示測試類別會繼承任何超類別宣告的位置和內嵌屬性。具體而言,測試類別的位置和內嵌屬性會附加到超類別宣告的位置和內嵌屬性。因此,子類別可以選擇擴充位置和內嵌屬性。請注意,稍後出現的屬性會遮蔽 (也就是說,覆寫) 較早出現的同名屬性。此外,上述優先順序規則也適用於繼承的測試屬性來源。

如果 @TestPropertySource 中的 inheritLocationsinheritProperties 屬性設定為 false,則測試類別的位置或內嵌屬性 (分別) 會遮蔽並有效地取代超類別定義的組態。

測試組態也可以從封閉類別繼承。如需詳細資訊,請參閱 @Nested 測試類別組態

在下一個範例中,BaseTestApplicationContext 僅使用 base.properties 檔案作為測試屬性來源載入。相反地,ExtendedTestApplicationContext 使用 base.propertiesextended.properties 檔案作為測試屬性來源位置載入。以下範例示範如何使用 properties 檔案在子類別及其超類別中定義屬性

  • Java

  • Kotlin

@TestPropertySource("base.properties")
@ContextConfiguration
class BaseTest {
	// ...
}

@TestPropertySource("extended.properties")
@ContextConfiguration
class ExtendedTest extends BaseTest {
	// ...
}
@TestPropertySource("base.properties")
@ContextConfiguration
open class BaseTest {
	// ...
}

@TestPropertySource("extended.properties")
@ContextConfiguration
class ExtendedTest : BaseTest() {
	// ...
}

在下一個範例中,BaseTestApplicationContext 僅使用內嵌 key1 屬性載入。相反地,ExtendedTestApplicationContext 使用內嵌 key1key2 屬性載入。以下範例示範如何使用內嵌屬性在子類別及其超類別中定義屬性

  • Java

  • Kotlin

@TestPropertySource(properties = "key1 = value1")
@ContextConfiguration
class BaseTest {
	// ...
}

@TestPropertySource(properties = "key2 = value2")
@ContextConfiguration
class ExtendedTest extends BaseTest {
	// ...
}
@TestPropertySource(properties = ["key1 = value1"])
@ContextConfiguration
open class BaseTest {
	// ...
}

@TestPropertySource(properties = ["key2 = value2"])
@ContextConfiguration
class ExtendedTest : BaseTest() {
	// ...
}