テストプロパティソースを使用したコンテキスト構成

Spring Framework は、プロパティソースの階層を持つ環境の概念に対するファーストクラスのサポートを備えており、テスト固有のプロパティソースを使用して統合テストを構成できます。@Configuration クラスで使用される @PropertySource アノテーションとは対照的に、テストクラスで @TestPropertySource アノテーションを宣言して、テストプロパティファイルまたはインラインプロパティのリソースの場所を宣言できます。これらのテストプロパティソースは、アノテーション付き統合テスト用に読み込まれた ApplicationContext の Environment の PropertySources のセットに追加されます。

@TestPropertySource は SmartContextLoader SPI のどの実装でも使用できますが、@TestPropertySource は古い ContextLoader SPI の実装ではサポートされていません。

SmartContextLoader の実装は、MergedContextConfiguration の getPropertySourceDescriptors() および getPropertySourceProperties() メソッドを介して、マージされたテストプロパティソース値にアクセスします。

テストプロパティソースの宣言

@TestPropertySource の locations または value 属性を使用して、テストプロパティファイルを構成できます。

デフォルトでは、従来の java.util.Properties ファイル形式と XML ベースの java.util.Properties ファイル形式 (たとえば、"classpath:/com/example/test.properties" または "file:///path/to/file.xml") の両方がサポートされています。Spring Framework 6.1 以降、JSON、YAML などの異なるファイル形式をサポートするために、@TestPropertySource の factory 属性を介してカスタム PropertySourceFactory を構成できます。

各パスは 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 絶対パスでプロパティファイルを指定します。

次の例に示すように、@TestPropertySource の properties 属性を使用して、キーと値のペアの形式でインラインプロパティを構成できます。すべてのキーと値のペアは、最高の優先順位を持つ単一のテスト PropertySource として、包含 Environment に追加されます。

キーと値のペアでサポートされている構文は、Java プロパティファイルのエントリに定義されている構文と同じです。

  • key=value

  • key:value

  • key value

プロパティは、上記の構文バリアントのいずれか、およびキーと値の間に任意の数のスペースを使用して定義できますが、テストスイート内で 1 つの構文バリアントと一貫したスペースを使用することをお勧めします。たとえば、常に key = value の代わりに key = value を使用することを検討してください。key= valuekey=value など。同様に、テキストブロックを使用してインラインプロパティを定義する場合は、テストスイート全体でインラインプロパティに一貫してテキストブロックを使用する必要があります。

その理由は、コンテキストキャッシュのキーを決定するために、指定した正確な文字列が使用されるためです。コンテキストキャッシュの利点を活用するには、インラインプロパティを一貫して定義する必要があります。

次の例では、2 つのインラインプロパティを設定します。

  • Java

  • Kotlin

@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port = 4242"}) (1)
class MyIntegrationTests {
	// class body...
}
1 文字列の配列を介して 2 つのプロパティを設定します。
@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port = 4242"]) (1)
class MyIntegrationTests {
	// class body...
}
1 文字列の配列を介して 2 つのプロパティを設定します。

Spring Framework 6.1 以降では、テキストブロックを使用して、単一の String で複数のインラインプロパティを定義できます。次の例では、テキストブロックを使用して 2 つのインラインプロパティを設定します。

  • Java

  • Kotlin

@ContextConfiguration
@TestPropertySource(properties = """
	timezone = GMT
	port = 4242
	""") (1)
class MyIntegrationTests {
	// class body...
}
1 テキストブロックを介して 2 つのプロパティを設定します。
@ContextConfiguration
@TestPropertySource(properties = ["""
	timezone = GMT
	port = 4242
	"""]) (1)
class MyIntegrationTests {
	// class body...
}
1 テキストブロックを介して 2 つのプロパティを設定します。

Spring Framework 5.2 以降、@TestPropertySource は繰り返し可能なアノテーションとして使用できます。つまり、単一のテストクラスで @TestPropertySource の複数の宣言を行うことができ、後の @TestPropertySource アノテーションの locations と properties が以前の @TestPropertySource アノテーションの @TestPropertySource をオーバーライドします。

さらに、それぞれが @TestPropertySource でメタアノテーションが付けられた、テストクラスで複数の合成アノテーションを宣言できます。これらのすべての @TestPropertySource 宣言は、テストプロパティソースに貢献します。

直接存在する @TestPropertySource アノテーションは、常にメタ存在する @TestPropertySource アノテーションよりも優先されます。つまり、直接存在する @TestPropertySource アノテーションからの locations および properties は、メタアノテーションとして使用される @TestPropertySource アノテーションからの locations および properties をオーバーライドします。

デフォルトのプロパティファイルの検出

@TestPropertySource が空のアノテーションとして宣言されている場合(つまり、locations または properties 属性の明示的な値がない場合)、アノテーションを宣言したクラスに関連するデフォルトのプロパティファイルを検出しようとします。例: アノテーション付きテストクラスが com.example.MyTest の場合、対応するデフォルトプロパティファイルは classpath:com/example/MyTest.properties です。デフォルトを検出できない場合、IllegalStateException がスローされます。

優先順位

テストプロパティは、オペレーティングシステムの環境、Java システムプロパティ、@PropertySource を使用してプログラムによってアプリケーションによって宣言的に追加されたプロパティソースで定義されたものよりも優先されます。テストプロパティを使用して、システムおよびアプリケーションプロパティソースからロードされたプロパティを選択的にオーバーライドできます。さらに、インライン化されたプロパティは、リソースの場所から読み込まれたプロパティよりも優先されます。ただし、@DynamicPropertySource を介して登録されたプロパティは、@TestPropertySource を介してロードされたプロパティよりも優先されることに注意してください。

次の例では、timezone プロパティと port プロパティ、および "/test.properties" で定義されたプロパティは、システムおよびアプリケーションプロパティソースで定義された同じ名前のプロパティをオーバーライドします。さらに、"/test.properties" ファイルが timezone および port プロパティのエントリを定義する場合、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 は、スーパークラスによって宣言されたプロパティファイルとインラインプロパティのリソースの場所を継承する必要があるかどうかを示すブール inheritLocations および inheritProperties 属性をサポートしています。両方のフラグのデフォルト値は true です。つまり、テストクラスは、スーパークラスによって宣言された場所とインラインプロパティを継承します。具体的には、テストクラスの場所とインラインプロパティは、スーパークラスによって宣言された場所とインラインプロパティに追加されます。サブクラスには、場所とインラインプロパティを継承するオプションがあります。後で表示されるプロパティは、以前に表示される同じ名前のプロパティをシャドウ(つまり、オーバーライド)することに注意してください。さらに、前述の優先規則は、継承されたテストプロパティソースにも適用されます。

@TestPropertySource の inheritLocations または inheritProperties 属性が false に設定されている場合、テストクラスシャドウの場所またはインラインプロパティはそれぞれ、スーパークラスによって定義された構成を効果的に置き換えます。

Spring Framework 5.3 以降、テスト構成は、囲んでいるクラスから継承することもできます。詳細については、@Nested テストクラスの構成を参照してください。

次の例では、BaseTest の ApplicationContext は、base.properties ファイルのみをテストプロパティソースとして使用してロードされます。対照的に、ExtendedTest の ApplicationContext は、base.properties および extended.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() {
	// ...
}

次の例では、インライン key1 プロパティのみを使用して、BaseTest の ApplicationContext がロードされます。対照的に、ExtendedTest の ApplicationContext は、インラインの key1 および key2 プロパティを使用してロードされます。次の例は、インラインプロパティを使用して、サブクラスとそのスーパークラスの両方でプロパティを定義する方法を示しています。

  • 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() {
	// ...
}