この章では、Spring の統合テストのサポートと単体テストのベストプラクティスについて説明します。Spring チームは、テスト駆動開発(TDD)を提唱しています。Spring チームは、Inversion of Control(IoC)を正しく使用すると、ユニットテストと統合テストの両方が簡単になることを確認しました(setter メソッドとクラス上の適切なコンストラクターが存在することで、テストを行う必要なく、簡単にワイヤリングできます)サービスロケータレジストリおよび同様の構造を設定します)。
2. ユニットテスト
依存性注入により、従来の Java EE 開発の場合よりも、コードがコンテナーに依存しにくくなります。アプリケーションを構成する POJO は、Spring またはその他のコンテナーなしで、new
演算子を使用してインスタンス化されたオブジェクトを使用して、JUnit または TestNG テストでテスト可能である必要があります。モックオブジェクトを (他の価値あるテスト手法と組み合わせて)使用して、コードを単独でテストできます。Spring のアーキテクチャの推奨事項に従うと、コードベースのクリーンなレイヤー化とコンポーネント化により、ユニットテストが容易になります。例:DAO またはリポジトリインターフェースをスタブまたはモックすることにより、単体テストの実行中に永続データにアクセスする必要なく、サービスレイヤーオブジェクトをテストできます。
通常、設定するランタイムインフラストラクチャがないため、真のユニットテストは非常に迅速に実行されます。開発方法論の一部として真の単体テストを強調すると、生産性を高めることができます。IoC ベースのアプリケーションの効果的な単体テストを作成するために、テストの章のこのセクションは必要ない場合があります。ただし、特定のユニットテストシナリオでは、Spring Framework はモックオブジェクトとテストサポートクラスを提供します。これらについては、この章で説明します。
2.1. モックオブジェクト
Spring には、モック専用のパッケージがいくつか含まれています。
2.1.1. 環境
org.springframework.mock.env
パッケージには、Environment
および PropertySource
抽象化のモック実装が含まれています(Bean 定義プロファイルおよび PropertySource
の抽象化を参照)。MockEnvironment
および MockPropertySource
は、環境固有のプロパティに依存するコードのコンテナー外テストの開発に役立ちます。
2.1.2. JNDI
org.springframework.mock.jndi
パッケージには、テストスイートまたはスタンドアロンアプリケーション用の単純な JNDI 環境をセットアップするために使用できる JNDI SPI の部分的な実装が含まれています。たとえば、JDBC DataSource
インスタンスが Java EE コンテナーの場合と同じテストコードで同じ JNDI 名にバインドされた場合、テストシナリオでアプリケーションコードと設定の両方を修正なしで再利用できます。
org.springframework.mock.jndi パッケージのモック JNDI サポートは、Simple-JNDI: GitHub (英語) などのサードパーティからの完全なソリューションを推奨して、Spring Framework 5.2 の時点で正式に非推奨になりました。 |
2.1.3. サーブレット API
org.springframework.mock.web
パッケージには、Web コンテキスト、コントローラー、フィルターのテストに役立つサーブレット API モックオブジェクトの包括的なセットが含まれています。これらのモックオブジェクトは、Spring の Web MVC フレームワークでの使用を対象としており、一般に動的なモックオブジェクト(EasyMock (英語) など)や代替の Servlet API モックオブジェクト(MockObjects (英語) など)よりも使いやすいです。
Spring Framework 5.0, 以降、org.springframework.mock.web のモックオブジェクトは Servlet 4.0 API に基づいています。 |
Spring MVC テストフレームワークは、モックサーブレット API オブジェクト上に構築され、Spring MVC の統合テストフレームワークを提供します。MockMvc を参照してください。
2.1.4. Spring Web リアクティブ
org.springframework.mock.http.server.reactive
パッケージには、WebFlux アプリケーションで使用するための ServerHttpRequest
および ServerHttpResponse
のモック実装が含まれています。org.springframework.mock.web.server
パッケージには、これらのモックリクエストおよびレスポンスオブジェクトに依存するモック ServerWebExchange
が含まれています。
MockServerHttpRequest
と MockServerHttpResponse
は両方とも、サーバー固有の実装と同じ抽象基本クラスから拡張され、それらと動作を共有します。例:作成されたモックリクエストは不変ですが、ServerHttpRequest
の mutate()
メソッドを使用して、変更されたインスタンスを作成できます。
モックレスポンスが書き込み契約を適切に実装し、書き込み完了ハンドル(つまり、Mono<Void>
)を返すために、デフォルトで Flux
と cache().then()
を使用します。これにより、データがバッファリングされ、テストでのアサーションに使用できるようになります。アプリケーションは、カスタム書き込み機能を設定できます(たとえば、無限ストリームをテストするため)。
WebTestClient は、モックのリクエストとレスポンスに基づいて構築され、HTTP サーバーなしで WebFlux アプリケーションのテストをサポートします。クライアントは、実行中のサーバーでのエンドツーエンドテストにも使用できます。
2.2. 単体テストのサポートクラス
Spring には、単体テストに役立つ多くのクラスが含まれています。それらは 2 つのカテゴリに分類されます。
2.2.1. 一般的なテストユーティリティ
org.springframework.test.util
パッケージには、単体テストおよび統合テストで使用するためのいくつかの汎用ユーティリティが含まれています。
ReflectionTestUtils
は、リフレクションベースのユーティリティメソッドのコレクションです。これらのメソッドは、アプリケーションコードのテスト時に定数の値の変更、public
以外のフィールドの設定、public
以外の setter メソッドの呼び出し、または public
以外の構成またはライフサイクルコールバックメソッドの呼び出しが必要なテストシナリオで使用できます。次のようなユースケース:
ドメインエンティティのプロパティの
public
setter メソッドではなく、private
またはprotected
フィールドアクセスを許可する ORM フレームワーク(JPA や Hibernate など)。private
またはprotected
フィールド、setter メソッド、構成メソッドの依存性注入を提供する Spring のアノテーション(@Autowired
、@Inject
、および@Resource
など)のサポート。ライフサイクルコールバックメソッドに対する
@PostConstruct
や@PreDestroy
などのアノテーションの使用。
AopTestUtils
(Javadoc) は、AOP 関連のユーティリティメソッドのコレクションです。これらのメソッドを使用して、1 つ以上の Spring プロキシの背後に隠されている基礎となるターゲットオブジェクトへの参照を取得できます。例:EasyMock や Mockito などのライブラリを使用して Bean を動的なモックとして構成しており、モックが Spring プロキシにラップされている場合、基礎となるモックに直接アクセスして、そのモックに期待を構成し、検証を実行する必要がある場合があります。Spring のコア AOP ユーティリティについては、AopUtils
(Javadoc) および AopProxyUtils
(Javadoc) を参照してください。
2.2.2. Spring MVC テストユーティリティ
org.springframework.test.web
パッケージには ModelAndViewAssert
(Javadoc) が含まれています。ModelAndViewAssert
(Javadoc) は、JUnit、TestNG、または Spring MVC ModelAndView
オブジェクトを処理する単体テスト用のその他のテストフレームワークと組み合わせて使用できます。
Spring MVC コントローラーの単体テスト Spring MVC Controller クラスを POJO として単体テストするには、Spring のサーブレット API モックから MockHttpServletRequest 、MockHttpSession と組み合わせた ModelAndViewAssert などを使用します。Spring MVC と REST Controller クラスの完全な統合テストを Spring MVC の WebApplicationContext 構成と組み合わせて行うには、代わりに Spring MVC テストフレームワークを使用してください。 |
3. 統合テスト
このセクション(この章の残りのほとんど)では、Spring アプリケーションの統合テストについて説明します。次のトピックが含まれます。
3.1. 概要
デプロイをアプリケーションサーバーに要求したり、他のエンタープライズインフラストラクチャに接続したりすることなく、いくつかの統合テストを実行できることが重要です。そうすることで、次のようなことをテストできます。
Spring IoC コンテナーコンテキストの正しい接続。
JDBC または ORM ツールを使用したデータアクセス。これには、SQL ステートメントの正確性、Hibernate クエリ、JPA エンティティマッピングなどが含まれます。
Spring Framework は、spring-test
モジュールでの統合テストの第一級のサポートを提供します。実際の JAR ファイルの名前には、リリースバージョンが含まれる場合があり、取得元によっては長い org.springframework.test
形式になる場合もあります(説明については、依存関係管理のセクションを参照してください)。このライブラリには org.springframework.test
パッケージが含まれています。org.springframework.test
パッケージには、Spring コンテナーとの統合テストに役立つ貴重なクラスが含まれています。このテストは、アプリケーションサーバーまたはその他のデプロイ環境に依存しません。このようなテストは、単体テストよりも実行に時間がかかりますが、アプリケーションサーバーに対してデプロイに依存する同等の Selenium テストまたはリモートテストよりもはるかに高速です。
ユニットおよび統合テストのサポートは、アノテーション駆動型 Spring TestContext フレームワークの形式で提供されます。TestContext フレームワークは、使用中の実際のテストフレームワークにとらわれないため、JUnit、TestNG などを含むさまざまな環境でのテストの計測が可能です。
3.2. 統合テストのゴール
Spring の統合テストサポートの主なゴールは次のとおりです。
テスト間で Spring IoC コンテナーキャッシングを管理します。
テストフィクスチャインスタンスの依存性注入を提供するため。
統合テストに適したトランザクション管理を提供するため。
開発者が統合テストを作成するのを支援する Spring 固有の基本クラスを提供します。
次のいくつかのセクションでは、各ゴールについて説明し、実装と構成の詳細へのリンクを提供します。
3.2.1. コンテキスト管理とキャッシュ
Spring TestContext フレームワークは、Spring ApplicationContext
インスタンスと WebApplicationContext
インスタンスの一貫したロード、およびこれらのコンテキストのキャッシングを提供します。Spring 自体のオーバーヘッドではなく、Spring コンテナーによってインスタンス化されたオブジェクトのインスタンス化に時間がかかるため、起動時間が課題になる可能性があるため、ロードされたコンテキストのキャッシュのサポートが重要です。例:Hibernate マッピングファイルが 50 から 100 のプロジェクトでは、マッピングファイルの読み込みに 10 から 20 秒かかる場合があり、すべてのテストフィクスチャですべてのテストを実行する前にそのコストが発生すると、全体的なテスト実行が遅くなり、開発者の生産性が低下します。
通常、テストクラスは、XML または Groovy 構成メタデータのリソースの場所の配列(多くの場合、クラスパス内)またはアプリケーションの構成に使用されるコンポーネントクラスの配列を宣言します。これらのロケーションまたはクラスは、web.xml
または実動デプロイのその他の構成ファイルで指定されているものと同じまたは類似しています。
デフォルトでは、ロードされると、構成された ApplicationContext
は各テストで再利用されます。セットアップコストはテストスイートごとに 1 回だけ発生し、その後のテスト実行ははるかに高速です。このコンテキストでは、「テストスイート」という用語は、すべてのテストが同じ JVM で実行されることを意味します。たとえば、特定のプロジェクトまたはモジュールの Ant、Maven、または Gradle ビルドから実行されるすべてのテストです。まれに、テストによってアプリケーションコンテキストが破損し、(たとえば、Bean 定義またはアプリケーションオブジェクトの状態を変更することによって)リロードが必要な場合、TestContext フレームワークは、次を実行する前に構成をリロードし、アプリケーションコンテキストを再構築するように構成できます。テスト。
TestContext フレームワークを使用したコンテキスト管理およびコンテキストキャッシングを参照してください。
3.2.2. テストフィクスチャの依存性注入
TestContext フレームワークがアプリケーションコンテキストをロードするとき、依存性注入を使用してテストクラスのインスタンスをオプションで構成できます。これは、アプリケーションコンテキストから事前に構成された Bean を使用して、テストフィクスチャをセットアップするための便利なメカニズムを提供します。ここでの大きな利点は、さまざまなテストシナリオ(たとえば、Spring 管理のオブジェクトグラフ、トランザクションプロキシ、DataSource
インスタンスなどの構成)でアプリケーションコンテキストを再利用できるため、個々のテストで複雑なテストフィクスチャセットアップを複製する必要がないことです。ケース。
例として、Title
ドメインエンティティのデータアクセスロジックを実装するクラス(HibernateTitleRepository
)があるシナリオを考えます。次の領域をテストする統合テストを作成します。
Spring 構成: 基本的に、
HibernateTitleRepository
Bean の構成に関連するものはすべて正しく存在していますか?Hibernate マッピングファイルの構成:すべてが正しくマッピングされており、適切な遅延読み込み設定が行われていますか?
HibernateTitleRepository
のロジック:このクラスの構成済みインスタンスは予想どおりに機能しますか?
テストフィクスチャと TestContext フレームワークの依存性注入を参照してください。
3.2.3. トランザクション管理
実際のデータベースにアクセスするテストの一般的な課題の 1 つは、永続ストアの状態への影響です。開発データベースを使用する場合でも、状態の変更は将来のテストに影響を与える場合があります。また、永続データの挿入や変更などの多くの操作は、トランザクションの外部では実行(検証)できません。
TestContext フレームワークはこの課題に対処します。デフォルトでは、フレームワークは各テストのトランザクションを作成してロールバックします。トランザクションの存在を想定できるコードを作成できます。テストでトランザクション的にプロキシされたオブジェクトを呼び出す場合、設定されたトランザクションのセマンティクスに従って、それらは正しく動作します。さらに、テスト用に管理されているトランザクション内で実行中にテストメソッドが選択したテーブルの内容を削除すると、デフォルトでトランザクションがロールバックされ、データベースはテスト実行前の状態に戻ります。テストのアプリケーションコンテキストで定義された PlatformTransactionManager
Bean を使用して、テストにトランザクションサポートが提供されます。
トランザクションをコミットする場合(通常ではありませんが、特定のテストでデータベースにデータを入力または変更する場合に役立つことがあります)、@Commit
アノテーションを使用して、TestContext フレームワークにトランザクションをロールバックする代わりにコミットさせるように指示できます。
TestContext フレームワークを使用したトランザクション管理を参照してください。
3.2.4. 統合テストのサポートクラス
Spring TestContext フレームワークは、統合テストの記述を簡素化するいくつかの abstract
サポートクラスを提供します。これらの基本テストクラスは、テストフレームワークへの明確に定義されたフックと、便利なインスタンス変数およびメソッドを提供します。
ApplicationContext
。明示的な Bean ルックアップを実行するか、コンテキスト全体の状態をテストします。SQL ステートメントを実行してデータベースを照会するための
JdbcTemplate
このようなクエリを使用して、データベース関連のアプリケーションコードの実行前と実行後の両方でデータベースの状態を確認できます。Spring は、そのようなクエリがアプリケーションコードと同じトランザクションのスコープで実行されるようにします。ORM ツールと組み合わせて使用する場合は、必ず誤検知を避けてください。
さらに、プロジェクト固有のインスタンス変数とメソッドを使用して、独自のカスタムアプリケーション全体のスーパークラスを作成することもできます。
TestContext フレームワークのサポートクラスを参照してください。
3.3. JDBC テストのサポート
org.springframework.test.jdbc
パッケージには JdbcTestUtils
が含まれています。JdbcTestUtils
は、標準のデータベーステストシナリオを簡素化することを目的とした JDBC 関連のユーティリティ関数のコレクションです。具体的には、JdbcTestUtils
は次の静的ユーティリティメソッドを提供します。
countRowsInTable(..)
: 指定されたテーブルの行数をカウントします。countRowsInTableWhere(..)
: 指定されたWHERE
句を使用して、指定されたテーブルの行数をカウントします。deleteFromTables(..)
: 指定されたテーブルからすべての行を削除します。deleteFromTableWhere(..)
: 指定されたWHERE
句を使用して、指定されたテーブルから行を削除します。dropTables(..)
: 指定したテーブルを削除します。
|
3.4. アノテーション
このセクションでは、Spring アプリケーションをテストするときに使用できるアノテーションについて説明します。次のトピックが含まれます。
3.4.1. Spring Test アノテーション
Spring Framework は、TestContext フレームワークと組み合わせてユニットおよび統合テストで使用できる次の Spring 固有のアノテーションセットを提供します。デフォルトの属性値、属性エイリアス、その他の詳細を含む詳細については、対応する javadoc を参照してください。
Spring のテストアノテーションには次のものが含まれます。
@BootstrapWith
@BootstrapWith
は、Spring TestContext フレームワークのブートストラップ方法を構成するために使用できるクラスレベルのアノテーションです。具体的には、@BootstrapWith
を使用してカスタム TestContextBootstrapper
を指定できます。詳細については、TestContext フレームワークのブートストラップに関するセクションを参照してください。
@ContextConfiguration
@ContextConfiguration
は、統合テスト用に ApplicationContext
をロードおよび構成する方法を決定するために使用されるクラスレベルのメタデータを定義します。具体的には、@ContextConfiguration
は、コンテキストのロードに使用されるアプリケーションコンテキストリソース locations
またはコンポーネント classes
を宣言します。
リソースの場所は通常、クラスパスにある XML 構成ファイルまたは Groovy スクリプトですが、コンポーネントクラスは通常 @Configuration
クラスです。ただし、リソースの場所はファイルシステム内のファイルとスクリプトを参照することもでき、コンポーネントクラスは @Component
クラス、@Service
クラスなどにすることができます。詳細については、コンポーネントクラスを参照してください。
次の例は、XML ファイルを参照する @ContextConfiguration
アノテーションを示しています。
@ContextConfiguration("/test-config.xml") (1)
class XmlApplicationContextTests {
// class body...
}
1 | XML ファイルを参照します。 |
@ContextConfiguration("/test-config.xml") (1)
class XmlApplicationContextTests {
// class body...
}
1 | XML ファイルを参照します。 |
次の例は、クラスを参照する @ContextConfiguration
アノテーションを示しています。
@ContextConfiguration(classes = TestConfig.class) (1)
class ConfigClassApplicationContextTests {
// class body...
}
1 | クラスを参照します。 |
@ContextConfiguration(classes = [TestConfig::class]) (1)
class ConfigClassApplicationContextTests {
// class body...
}
1 | クラスを参照します。 |
リソースの場所またはコンポーネントクラスを宣言する代わりに、またはそれに加えて、@ContextConfiguration
を使用して ApplicationContextInitializer
クラスを宣言できます。次の例は、このような場合を示しています。
@ContextConfiguration(initializers = CustomContextIntializer.class) (1)
class ContextInitializerTests {
// class body...
}
@ContextConfiguration(initializers = [CustomContextIntializer::class]) (1)
class ContextInitializerTests {
// class body...
}
1 | 初期化子クラスを宣言します。 |
オプションで @ContextConfiguration
を使用して、ContextLoader
戦略を宣言することもできます。ただし、デフォルトのローダーは initializers
およびリソース locations
またはコンポーネント classes
のいずれかをサポートするため、通常はローダーを明示的に構成する必要はありません。
次の例では、場所とローダーの両方を使用しています。
@ContextConfiguration(locations = "/test-context.xml", loader = CustomContextLoader.class) (1)
class CustomLoaderXmlApplicationContextTests {
// class body...
}
1 | ロケーションとカスタムローダーの両方を設定します。 |
@ContextConfiguration("/test-context.xml", loader = CustomContextLoader::class) (1)
class CustomLoaderXmlApplicationContextTests {
// class body...
}
1 | ロケーションとカスタムローダーの両方を設定します。 |
@ContextConfiguration は、リソースの場所または構成クラス、およびスーパークラスまたはそれを囲むクラスによって宣言されたコンテキスト初期化子の継承をサポートします。 |
詳細については、コンテキスト管理、@Nested
テストクラスの構成、@ContextConfiguration
javadoc を参照してください。
@WebAppConfiguration
@WebAppConfiguration
は、統合テスト用にロードされた ApplicationContext
が WebApplicationContext
であることを宣言するために使用できるクラスレベルのアノテーションです。テストクラスに @WebAppConfiguration
が存在するだけで、Web アプリケーションのルートへのパス(つまり、リソースベースパス)に "file:src/main/webapp"
のデフォルト値を使用して、WebApplicationContext
がテスト用にロードされます。リソースベースパスは、テストの WebApplicationContext
の ServletContext
として機能する MockServletContext
を作成するためにバックグラウンドで使用されます。
次の例は、@WebAppConfiguration
アノテーションの使用方法を示しています。
@ContextConfiguration
@WebAppConfiguration (1)
class WebAppTests {
// class body...
}
@ContextConfiguration
@WebAppConfiguration (1)
class WebAppTests {
// class body...
}
1 | @WebAppConfiguration アノテーション。 |
デフォルトをオーバーライドするには、暗黙の value
属性を使用して、異なるベースリソースパスを指定できます。classpath:
と file:
の両方のリソースプレフィックスがサポートされています。リソースプレフィックスが指定されていない場合、パスはファイルシステムリソースであると見なされます。次の例は、クラスパスリソースを指定する方法を示しています。
@ContextConfiguration
@WebAppConfiguration("classpath:test-web-resources") (1)
class WebAppTests {
// class body...
}
1 | クラスパスリソースの指定。 |
@ContextConfiguration
@WebAppConfiguration("classpath:test-web-resources") (1)
class WebAppTests {
// class body...
}
1 | クラスパスリソースの指定。 |
@WebAppConfiguration
は、単一のテストクラス内またはテストクラス階層内で、@ContextConfiguration
と組み合わせて使用する必要があることに注意してください。詳細については、@WebAppConfiguration
(Javadoc) javadoc を参照してください。
@ContextHierarchy
@ContextHierarchy
は、統合テスト用に ApplicationContext
インスタンスの階層を定義するために使用されるクラスレベルのアノテーションです。@ContextHierarchy
は、1 つまたは複数の @ContextConfiguration
インスタンスのリストで宣言する必要があります。各インスタンスは、コンテキスト階層のレベルを定義します。次の例は、単一のテストクラス内で @ContextHierarchy
を使用する方法を示しています(@ContextHierarchy
はテストクラス階層内でも使用できます)。
@ContextHierarchy({
@ContextConfiguration("/parent-config.xml"),
@ContextConfiguration("/child-config.xml")
})
class ContextHierarchyTests {
// class body...
}
@ContextHierarchy(
ContextConfiguration("/parent-config.xml"),
ContextConfiguration("/child-config.xml"))
class ContextHierarchyTests {
// class body...
}
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = AppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
class WebIntegrationTests {
// class body...
}
@WebAppConfiguration
@ContextHierarchy(
ContextConfiguration(classes = [AppConfig::class]),
ContextConfiguration(classes = [WebConfig::class]))
class WebIntegrationTests {
// class body...
}
テストクラス階層内のコンテキスト階層の特定のレベルの構成をマージまたはオーバーライドする必要がある場合は、クラス階層の対応する各レベルで @ContextConfiguration
の name
属性に同じ値を指定して、そのレベルに明示的に名前を付ける必要があります。さらなる例については、コンテキストの階層および @ContextHierarchy
(Javadoc) javadoc を参照してください。
@ActiveProfiles
@ActiveProfiles
は、統合テスト用に ApplicationContext
をロードするときに、どの Bean 定義プロファイルをアクティブにする必要があるかを宣言するために使用されるクラスレベルのアノテーションです。
次の例は、dev
プロファイルがアクティブであることを示しています。
@ContextConfiguration
@ActiveProfiles("dev") (1)
class DeveloperTests {
// class body...
}
1 | dev プロファイルがアクティブであることを示します。 |
@ContextConfiguration
@ActiveProfiles("dev") (1)
class DeveloperTests {
// class body...
}
1 | dev プロファイルがアクティブであることを示します。 |
次の例は、dev
プロファイルと integration
プロファイルの両方がアクティブであることを示しています。
@ContextConfiguration
@ActiveProfiles({"dev", "integration"}) (1)
class DeveloperIntegrationTests {
// class body...
}
1 | dev および integration プロファイルがアクティブであることを示します。 |
@ContextConfiguration
@ActiveProfiles(["dev", "integration"]) (1)
class DeveloperIntegrationTests {
// class body...
}
1 | dev および integration プロファイルがアクティブであることを示します。 |
@ActiveProfiles は、スーパークラスによって宣言されたアクティブな Bean 定義プロファイルを継承し、デフォルトでクラスを囲むためのサポートを提供します。カスタム ActiveProfilesResolver を実装し、@ActiveProfiles の resolver 属性を使用して登録することにより、アクティブな Bean 定義プロファイルをプログラムで解決することもできます。 |
例と詳細については、環境プロファイルを使用したコンテキスト設定、@Nested
テストクラスの構成、@ActiveProfiles
(Javadoc) javadoc を参照してください。
@TestPropertySource
@TestPropertySource
は、統合テスト用に読み込まれた ApplicationContext
の Environment
の PropertySources
のセットに追加されるプロパティファイルとインラインプロパティの場所を構成するために使用できるクラスレベルのアノテーションです。
次の例は、クラスパスからプロパティファイルを宣言する方法を示しています。
@ContextConfiguration
@TestPropertySource("/test.properties") (1)
class MyIntegrationTests {
// class body...
}
1 | クラスパスのルートにある test.properties からプロパティを取得します。 |
@ContextConfiguration
@TestPropertySource("/test.properties") (1)
class MyIntegrationTests {
// class body...
}
1 | クラスパスのルートにある test.properties からプロパティを取得します。 |
次の例は、インラインプロパティを宣言する方法を示しています。
@ContextConfiguration
@TestPropertySource(properties = { "timezone = GMT", "port: 4242" }) (1)
class MyIntegrationTests {
// class body...
}
1 | timezone および port プロパティを宣言します。 |
@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port: 4242"]) (1)
class MyIntegrationTests {
// class body...
}
1 | timezone および port プロパティを宣言します。 |
例と詳細については、テストプロパティソースを使用したコンテキスト構成を参照してください。
@DynamicPropertySource
@DynamicPropertySource
は、統合テスト用にロードされた ApplicationContext
の Environment
の PropertySources
のセットに追加される動的プロパティを登録するために使用できるメソッドレベルのアノテーションです。動的プロパティは、プロパティの値が事前にわからない場合に役立ちます。たとえば、プロパティがテストコンテナー (英語) プロジェクトによって管理されるコンテナーなどの外部リソースによって管理される場合です。
次の例は、動的プロパティを登録する方法を示しています。
@ContextConfiguration
class MyIntegrationTests {
static MyExternalServer server = // ...
@DynamicPropertySource (1)
static void dynamicProperties(DynamicPropertyRegistry registry) { (2)
registry.add("server.port", server::getPort); (3)
}
// tests ...
}
1 | @DynamicPropertySource で static メソッドにアノテーションを付けます。 |
2 | 引数として DynamicPropertyRegistry を受け入れます。 |
3 | サーバーから遅延取得される動的 server.port プロパティを登録します。 |
@ContextConfiguration
class MyIntegrationTests {
companion object {
@JvmStatic
val server: MyExternalServer = // ...
@DynamicPropertySource (1)
@JvmStatic
fun dynamicProperties(registry: DynamicPropertyRegistry) { (2)
registry.add("server.port", server::getPort) (3)
}
}
// tests ...
}
1 | @DynamicPropertySource で static メソッドにアノテーションを付けます。 |
2 | 引数として DynamicPropertyRegistry を受け入れます。 |
3 | サーバーから遅延取得される動的 server.port プロパティを登録します。 |
詳細については、動的プロパティソースを使用したコンテキスト構成を参照してください。
@DirtiesContext
@DirtiesContext
は、基礎となる Spring ApplicationContext
がテストの実行中にダーティになったこと(つまり、テストが何らかの方法で変更または破損した - シングルトン Bean の状態を変更するなど)を閉じる必要があることを示します。アプリケーションコンテキストがダーティとしてマークされると、テストフレームワークのキャッシュから削除され、閉じられます。結果として、基盤となる Spring コンテナーは、同じ構成メタデータを持つコンテキストを必要とする後続のテストのために再構築されます。
@DirtiesContext
は、同じクラスまたはクラス階層内でクラスレベルとメソッドレベルの両方のアノテーションとして使用できます。このようなシナリオでは、ApplicationContext
は、構成された methodMode
および classMode
に応じて、そのようなアノテーション付きメソッドの前後または現在のテストクラスの前後にダーティとしてマークされます。
次の例は、さまざまな構成シナリオでコンテキストがダーティになる場合を説明しています。
現在のテストクラスの前で、クラスモードが
BEFORE_CLASS
に設定されているクラスで宣言された場合。Java@DirtiesContext(classMode = BEFORE_CLASS) (1) class FreshContextTests { // some tests that require a new Spring container }
1 現在のテストクラスの前のコンテキストをダーティにします。 Kotlin@DirtiesContext(classMode = BEFORE_CLASS) (1) class FreshContextTests { // some tests that require a new Spring container }
1 現在のテストクラスの前のコンテキストをダーティにします。 現在のテストクラスの後、クラスモードが
AFTER_CLASS
(つまり、デフォルトクラスモード)に設定されたクラスで宣言された場合。Java@DirtiesContext (1) class ContextDirtyingTests { // some tests that result in the Spring container being dirtied }
1 現在のテストクラスの後にコンテキストをダーティにします。 Kotlin@DirtiesContext (1) class ContextDirtyingTests { // some tests that result in the Spring container being dirtied }
1 現在のテストクラスの後にコンテキストをダーティにします。 クラスモードが
BEFORE_EACH_TEST_METHOD.
に設定されたクラスで宣言された場合、現在のテストクラスの各テストメソッドの前Java@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD) (1) class FreshContextTests { // some tests that require a new Spring container }
1 各テストメソッドの前にコンテキストをダーティにします。 Kotlin@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD) (1) class FreshContextTests { // some tests that require a new Spring container }
1 各テストメソッドの前にコンテキストをダーティにします。 現在のテストクラスの各テストメソッドの後、クラスモードが
AFTER_EACH_TEST_METHOD.
に設定されたクラスで宣言された場合Java@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) (1) class ContextDirtyingTests { // some tests that result in the Spring container being dirtied }
1 各テストメソッドの後にコンテキストをダーティにします。 Kotlin@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) (1) class ContextDirtyingTests { // some tests that result in the Spring container being dirtied }
1 各テストメソッドの後にコンテキストをダーティにします。 現在のテストの前、メソッドモードが
BEFORE_METHOD
に設定されたメソッドで宣言された場合。Java@DirtiesContext(methodMode = BEFORE_METHOD) (1) @Test void testProcessWhichRequiresFreshAppCtx() { // some logic that requires a new Spring container }
1 現在のテストメソッドの前のコンテキストをダーティにします。 Kotlin@DirtiesContext(methodMode = BEFORE_METHOD) (1) @Test fun testProcessWhichRequiresFreshAppCtx() { // some logic that requires a new Spring container }
1 現在のテストメソッドの前のコンテキストをダーティにします。 現在のテストの後、メソッドモードが
AFTER_METHOD
(つまり、デフォルトのメソッドモード)に設定されたメソッドで宣言されたとき。Java@DirtiesContext (1) @Test void testProcessWhichDirtiesAppCtx() { // some logic that results in the Spring container being dirtied }
1 現在のテストメソッドの後にコンテキストをダーティにします。 Kotlin@DirtiesContext (1) @Test fun testProcessWhichDirtiesAppCtx() { // some logic that results in the Spring container being dirtied }
1 現在のテストメソッドの後にコンテキストをダーティにします。
@ContextHierarchy
を使用してコンテキストがコンテキスト階層の一部として構成されているテストで @DirtiesContext
を使用する場合、hierarchyMode
フラグを使用してコンテキストキャッシュのクリア方法を制御できます。デフォルトでは、現在のレベルだけでなく、現在のテストに共通の祖先コンテキストを共有する他のすべてのコンテキスト階層も含めて、網羅的アルゴリズムを使用してコンテキストキャッシュをクリアします。共通の祖先コンテキストのサブ階層にあるすべての ApplicationContext
インスタンスは、コンテキストキャッシュから削除され、閉じられます。特定のユースケースで徹底的なアルゴリズムが過剰である場合、次の例に示すように、より単純な現在のレベルのアルゴリズムを指定できます。
@ContextHierarchy({
@ContextConfiguration("/parent-config.xml"),
@ContextConfiguration("/child-config.xml")
})
class BaseTests {
// class body...
}
class ExtendedTests extends BaseTests {
@Test
@DirtiesContext(hierarchyMode = CURRENT_LEVEL) (1)
void test() {
// some logic that results in the child context being dirtied
}
}
1 | 現在のレベルのアルゴリズムを使用します。 |
@ContextHierarchy(
ContextConfiguration("/parent-config.xml"),
ContextConfiguration("/child-config.xml"))
open class BaseTests {
// class body...
}
class ExtendedTests : BaseTests() {
@Test
@DirtiesContext(hierarchyMode = CURRENT_LEVEL) (1)
fun test() {
// some logic that results in the child context being dirtied
}
}
1 | 現在のレベルのアルゴリズムを使用します。 |
EXHAUSTIVE
および CURRENT_LEVEL
アルゴリズムの詳細については、DirtiesContext.HierarchyMode
(Javadoc) javadoc を参照してください。
@TestExecutionListeners
@TestExecutionListeners
は、TestContextManager
に登録する必要がある TestExecutionListener
実装を構成するためのクラスレベルのメタデータを定義します。通常、@TestExecutionListeners
は @ContextConfiguration
と組み合わせて使用されます。
次の例は、2 つの TestExecutionListener
実装を登録する方法を示しています。
@ContextConfiguration
@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class}) (1)
class CustomTestExecutionListenerTests {
// class body...
}
1 | 2 つの TestExecutionListener 実装を登録します。 |
@ContextConfiguration
@TestExecutionListeners(CustomTestExecutionListener::class, AnotherTestExecutionListener::class) (1)
class CustomTestExecutionListenerTests {
// class body...
}
1 | 2 つの TestExecutionListener 実装を登録します。 |
デフォルトでは、@TestExecutionListeners
は、スーパークラスまたはそれを囲むクラスからリスナーを継承するためのサポートを提供します。例と詳細については、@Nested
テストクラスの構成と @TestExecutionListeners
javadoc を参照してください。
@RecordApplicationEvents
@RecordApplicationEvents
は、単一のテストの実行中に ApplicationContext
で公開されたすべてのアプリケーションイベントを記録するように Spring TestContext フレームワークに指示するために使用されるクラスレベルのアノテーションです。
記録されたイベントには、テスト内の ApplicationEvents
API を介してアクセスできます。
例と詳細については、アプリケーションイベントと @RecordApplicationEvents
javadoc を参照してください。
@Commit
@Commit
は、テストメソッドが完了した後に、トランザクションテストメソッドのトランザクションをコミットする必要があることを示します。@Commit
を @Rollback(false)
の直接の代替として使用して、コードの意図をより明確に伝えることができます。@Rollback
と同様に、@Commit
はクラスレベルまたはメソッドレベルのアノテーションとして宣言することもできます。
次の例は、@Commit
アノテーションの使用方法を示しています。
@Commit (1)
@Test
void testProcessWithoutRollback() {
// ...
}
1 | テストの結果をデータベースにコミットします。 |
@Commit (1)
@Test
fun testProcessWithoutRollback() {
// ...
}
1 | テストの結果をデータベースにコミットします。 |
@Rollback
@Rollback
は、テストメソッドの補完後にトランザクションテストメソッドのトランザクションをロールバックする必要があるかどうかを示します。true
の場合、トランザクションはロールバックされます。それ以外の場合、トランザクションはコミットされます(@Commit
も参照)。@Rollback
が明示的に宣言されていない場合でも、Spring TestContext フレームワークの統合テストのロールバックはデフォルトで true
になります。
@Rollback
は、クラスレベルのアノテーションとして宣言されると、テストクラス階層内のすべてのテストメソッドのデフォルトロールバックセマンティクスを定義します。メソッドレベルのアノテーションとして宣言された場合、@Rollback
は特定のテストメソッドのロールバックセマンティクスを定義し、潜在的にクラスレベルの @Rollback
または @Commit
セマンティクスをオーバーライドします。
次の例では、テストメソッドの結果はロールバックされません(つまり、結果はデータベースにコミットされます)。
@Rollback(false) (1)
@Test
void testProcessWithoutRollback() {
// ...
}
1 | 結果をロールバックしないでください。 |
@Rollback(false) (1)
@Test
fun testProcessWithoutRollback() {
// ...
}
1 | 結果をロールバックしないでください。 |
@BeforeTransaction
@BeforeTransaction
は、Spring の @Transactional
アノテーションを使用してトランザクション内で実行するように構成されたテストメソッドの場合、トランザクションを開始する前にアノテーション付き void
メソッドを実行する必要があることを示します。@BeforeTransaction
メソッドは public
である必要はなく、Java 8 ベースのインターフェースのデフォルトメソッドで宣言できます。
次の例は、@BeforeTransaction
アノテーションの使用方法を示しています。
@BeforeTransaction (1)
void beforeTransaction() {
// logic to be run before a transaction is started
}
1 | トランザクションの前にこのメソッドを実行します。 |
@BeforeTransaction (1)
fun beforeTransaction() {
// logic to be run before a transaction is started
}
1 | トランザクションの前にこのメソッドを実行します。 |
@AfterTransaction
@AfterTransaction
は、Spring の @Transactional
アノテーションを使用してトランザクション内で実行するように構成されたテストメソッドについて、トランザクションの終了後にアノテーション付き void
メソッドを実行する必要があることを示します。@AfterTransaction
メソッドは public
である必要はなく、Java 8 ベースのインターフェースのデフォルトメソッドで宣言できます。
@AfterTransaction (1)
void afterTransaction() {
// logic to be run after a transaction has ended
}
1 | トランザクションの後にこのメソッドを実行します。 |
@AfterTransaction (1)
fun afterTransaction() {
// logic to be run after a transaction has ended
}
1 | トランザクションの後にこのメソッドを実行します。 |
@Sql
@Sql
は、テストクラスまたはテストメソッドにアノテーションを付けて、統合テスト中に特定のデータベースに対して実行される SQL スクリプトを構成するために使用されます。次の例は、その使用方法を示しています。
@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"}) (1)
void userTest() {
// run code that relies on the test schema and test data
}
1 | このテスト用に 2 つのスクリプトを実行します。 |
@Test
@Sql("/test-schema.sql", "/test-user-data.sql") (1)
fun userTest() {
// run code that relies on the test schema and test data
}
1 | このテスト用に 2 つのスクリプトを実行します。 |
詳細については、@Sql を使用して SQL スクリプトを宣言的に実行するを参照してください。
@SqlConfig
@SqlConfig
は、@Sql
アノテーションで構成された SQL スクリプトを解析および実行する方法を決定するために使用されるメタデータを定義します。次の例は、その使用方法を示しています。
@Test
@Sql(
scripts = "/test-user-data.sql",
config = @SqlConfig(commentPrefix = "`", separator = "@@") (1)
)
void userTest() {
// run code that relies on the test data
}
1 | SQL スクリプトでコメントプレフィックスとセパレータを設定します。 |
@Test
@Sql("/test-user-data.sql", config = SqlConfig(commentPrefix = "`", separator = "@@")) (1)
fun userTest() {
// run code that relies on the test data
}
1 | SQL スクリプトでコメントプレフィックスとセパレータを設定します。 |
@SqlMergeMode
@SqlMergeMode
は、テストクラスまたはテストメソッドにアノテーションを付けて、メソッドレベルの @Sql
宣言をクラスレベルの @Sql
宣言とマージするかどうかを構成するために使用されます。@SqlMergeMode
がテストクラスまたはテストメソッドで宣言されていない場合、OVERRIDE
マージモードがデフォルトで使用されます。OVERRIDE
モードでは、メソッドレベルの @Sql
宣言がクラスレベルの @Sql
宣言を効果的にオーバーライドします。
メソッドレベルの @SqlMergeMode
宣言は、クラスレベルの宣言をオーバーライドすることに注意してください。
次の例は、クラスレベルで @SqlMergeMode
を使用する方法を示しています。
@SpringJUnitConfig(TestConfig.class)
@Sql("/test-schema.sql")
@SqlMergeMode(MERGE) (1)
class UserTests {
@Test
@Sql("/user-test-data-001.sql")
void standardUserProfile() {
// run code that relies on test data set 001
}
}
1 | クラス内のすべてのテストメソッドについて、@Sql マージモードを MERGE に設定します。 |
@SpringJUnitConfig(TestConfig::class)
@Sql("/test-schema.sql")
@SqlMergeMode(MERGE) (1)
class UserTests {
@Test
@Sql("/user-test-data-001.sql")
fun standardUserProfile() {
// run code that relies on test data set 001
}
}
1 | クラス内のすべてのテストメソッドについて、@Sql マージモードを MERGE に設定します。 |
次の例は、メソッドレベルで @SqlMergeMode
を使用する方法を示しています。
@SpringJUnitConfig(TestConfig.class)
@Sql("/test-schema.sql")
class UserTests {
@Test
@Sql("/user-test-data-001.sql")
@SqlMergeMode(MERGE) (1)
void standardUserProfile() {
// run code that relies on test data set 001
}
}
1 | 特定のテストメソッドの @Sql マージモードを MERGE に設定します。 |
@SpringJUnitConfig(TestConfig::class)
@Sql("/test-schema.sql")
class UserTests {
@Test
@Sql("/user-test-data-001.sql")
@SqlMergeMode(MERGE) (1)
fun standardUserProfile() {
// run code that relies on test data set 001
}
}
1 | 特定のテストメソッドの @Sql マージモードを MERGE に設定します。 |
@SqlGroup
@SqlGroup
は、いくつかの @Sql
アノテーションを集約するコンテナーアノテーションです。@SqlGroup
をネイティブで使用して、複数のネストされた @Sql
アノテーションを宣言するか、Java 8 の反復可能なアノテーションのサポートと組み合わせて使用できます。@Sql
は、同じクラスまたはメソッドで何度も宣言でき、暗黙的にこのコンテナーアノテーションを生成します。次の例は、SQL グループを宣言する方法を示しています。
@Test
@SqlGroup({ (1)
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
@Sql("/test-user-data.sql")
)}
void userTest() {
// run code that uses the test schema and test data
}
1 | SQL スクリプトのグループを宣言します。 |
@Test
@SqlGroup( (1)
Sql("/test-schema.sql", config = SqlConfig(commentPrefix = "`")),
Sql("/test-user-data.sql"))
fun userTest() {
// run code that uses the test schema and test data
}
1 | SQL スクリプトのグループを宣言します。 |
3.4.2. 標準アノテーションのサポート
以下のアノテーションは、Spring TestContext フレームワークのすべての構成の標準セマンティクスでサポートされています。これらのアノテーションはテスト固有のものではなく、Spring Framework のどこでも使用できることに注意してください。
@Autowired
@Qualifier
@Value
JSR-250 が存在する場合、
@Resource
(javax.annotation)JSR-250 が存在する場合、
@ManagedBean
(javax.annotation)JSR-330 が存在する場合、
@Inject
(javax.inject)JSR-330 が存在する場合、
@Named
(javax.inject)JPA が存在する場合、
@PersistenceContext
(javax.persistence)JPA が存在する場合、
@PersistenceUnit
(javax.persistence)@Required
@Transactional
(org.springframework.transaction.annotation)と 属性サポートの制限
JSR-250 ライフサイクルアノテーション Spring TestContext フレームワークでは、 テストクラス内のメソッドに |
3.4.3. Spring JUnit 4 テストアノテーション
次のアノテーションは、SpringRunner、Spring の JUnit 4 ルール、または Spring の JUnit 4 サポートクラスと組み合わせて使用される場合にのみサポートされます。
@IfProfileValue
@IfProfileValue
は、特定のテスト環境でアノテーション付きテストが有効になっていることを示します。設定された ProfileValueSource
が、提供された name
に一致する value
を返す場合、テストは有効です。そうでない場合、テストは無効になり、事実上無視されます。
@IfProfileValue
は、クラスレベル、メソッドレベル、またはその両方で適用できます。@IfProfileValue
のクラスレベルの使用は、そのクラスまたはそのサブクラス内のメソッドのメソッドレベルの使用よりも優先されます。具体的には、テストは、クラスレベルとメソッドレベルの両方で有効になっている場合に有効になります。@IfProfileValue
がないと、テストは暗黙的に有効になります。これは、JUnit 4 の @Ignore
アノテーションのセマンティクスに類似していますが、@Ignore
が存在すると常にテストが無効になる点が異なります。
次の例は、@IfProfileValue
アノテーションを持つテストを示しています。
@IfProfileValue(name="java.vendor", value="Oracle Corporation") (1)
@Test
public void testProcessWhichRunsOnlyOnOracleJvm() {
// some logic that should run only on Java VMs from Oracle Corporation
}
1 | このテストは、Java ベンダーが「Oracle Corporation」の場合にのみ実行してください。 |
@IfProfileValue(name="java.vendor", value="Oracle Corporation") (1)
@Test
fun testProcessWhichRunsOnlyOnOracleJvm() {
// some logic that should run only on Java VMs from Oracle Corporation
}
1 | このテストは、Java ベンダーが「Oracle Corporation」の場合にのみ実行してください。 |
または、@IfProfileValue
を values
のリスト(OR
セマンティクスを使用)で構成して、JUnit 4 環境のテストグループに対する TestNG のようなサポートを実現できます。次の例を考えてみましょう。
@IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"}) (1)
@Test
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
// some logic that should run only for unit and integration test groups
}
1 | 単体テストと統合テストに対してこのテストを実行します。 |
@IfProfileValue(name="test-groups", values=["unit-tests", "integration-tests"]) (1)
@Test
fun testProcessWhichRunsForUnitOrIntegrationTestGroups() {
// some logic that should run only for unit and integration test groups
}
1 | 単体テストと統合テストに対してこのテストを実行します。 |
@ProfileValueSourceConfiguration
@ProfileValueSourceConfiguration
は、@IfProfileValue
アノテーションで構成されたプロファイル値を取得するときに使用する ProfileValueSource
のタイプを指定するクラスレベルのアノテーションです。@ProfileValueSourceConfiguration
がテスト用に宣言されていない場合、デフォルトで SystemProfileValueSource
が使用されます。次の例は、@ProfileValueSourceConfiguration
の使用方法を示しています。
@ProfileValueSourceConfiguration(CustomProfileValueSource.class) (1)
public class CustomProfileValueSourceTests {
// class body...
}
1 | カスタムプロファイル値ソースを使用します。 |
@ProfileValueSourceConfiguration(CustomProfileValueSource::class) (1)
class CustomProfileValueSourceTests {
// class body...
}
1 | カスタムプロファイル値ソースを使用します。 |
@Timed
@Timed
は、アノテーション付きのテストメソッドが指定された期間(ミリ秒単位)で実行を終了する必要があることを示します。テキストの実行時間が指定された期間を超えると、テストは失敗します。
期間には、テストメソッド自体の実行、テストの繰り返し(@Repeat
を参照)、テストフィクスチャの設定または破棄が含まれます。次の例は、その使用方法を示しています。
@Timed(millis = 1000) (1)
public void testProcessWithOneSecondTimeout() {
// some logic that should not take longer than 1 second to run
}
1 | テストの期間を 1 秒に設定します。 |
@Timed(millis = 1000) (1)
fun testProcessWithOneSecondTimeout() {
// some logic that should not take longer than 1 second to run
}
1 | テストの期間を 1 秒に設定します。 |
Spring の @Timed
アノテーションのセマンティクスは、JUnit 4 の @Test(timeout=…)
サポートとは異なります。具体的には、JUnit 4 がテスト実行タイムアウトを処理する方法により(つまり、別の Thread
でテストメソッドを実行することにより)、テストに時間がかかりすぎると @Test(timeout=…)
はテストをプリエンプティブに失敗します。一方、Spring の @Timed
は、テストを先制的に失敗させるのではなく、失敗する前にテストの補完を待機します。
@Repeat
@Repeat
は、アノテーション付きテストメソッドを繰り返し実行する必要があることを示します。テストメソッドが実行される回数は、アノテーションで指定されます。
繰り返される実行の範囲には、テストフィクスチャのセットアップまたは破棄だけでなく、テストメソッド自体の実行も含まれます。次の例は、@Repeat
アノテーションの使用方法を示しています。
@Repeat(10) (1)
@Test
public void testProcessRepeatedly() {
// ...
}
1 | このテストを 10 回繰り返します。 |
@Repeat(10) (1)
@Test
fun testProcessRepeatedly() {
// ...
}
1 | このテストを 10 回繰り返します。 |
3.4.4. Spring JUnit Jupiter テストアノテーション
以下のアノテーションは、SpringExtension
および JUnit Jupiter(つまり、JUnit 5 のプログラミングモデル)と組み合わせて使用するとサポートされます。
@SpringJUnitConfig
@SpringJUnitConfig
は、JUnit Jupiter の @ExtendWith(SpringExtension.class)
と Spring TestContext フレームワークの @ContextConfiguration
を組み合わせた合成アノテーションです。クラスレベルで @ContextConfiguration
のドロップイン置換として使用できます。構成オプションに関して、@ContextConfiguration
と @SpringJUnitConfig
の唯一の違いは、@SpringJUnitConfig
の value
属性でコンポーネントクラスを宣言できることです。
次の例は、@SpringJUnitConfig
アノテーションを使用して構成クラスを指定する方法を示しています。
@SpringJUnitConfig(TestConfig.class) (1)
class ConfigurationClassJUnitJupiterSpringTests {
// class body...
}
1 | 構成クラスを指定します。 |
@SpringJUnitConfig(TestConfig::class) (1)
class ConfigurationClassJUnitJupiterSpringTests {
// class body...
}
1 | 構成クラスを指定します。 |
次の例は、@SpringJUnitConfig
アノテーションを使用して構成ファイルの場所を指定する方法を示しています。
@SpringJUnitConfig(locations = "/test-config.xml") (1)
class XmlJUnitJupiterSpringTests {
// class body...
}
1 | 構成ファイルの場所を指定します。 |
@SpringJUnitConfig(locations = ["/test-config.xml"]) (1)
class XmlJUnitJupiterSpringTests {
// class body...
}
1 | 構成ファイルの場所を指定します。 |
詳細については、コンテキスト管理および @SpringJUnitConfig
(Javadoc) および @ContextConfiguration
の javadoc を参照してください。
@SpringJUnitWebConfig
@SpringJUnitWebConfig
は、JUnit Jupiter の @ExtendWith(SpringExtension.class)
と Spring TestContext フレームワークの @ContextConfiguration
および @WebAppConfiguration
を組み合わせた合成アノテーションです。クラスレベルで、@ContextConfiguration
および @WebAppConfiguration
のドロップイン置換として使用できます。構成オプションに関して、@ContextConfiguration
と @SpringJUnitWebConfig
の唯一の違いは、@SpringJUnitWebConfig
の value
属性を使用してコンポーネントクラスを宣言できることです。さらに、@SpringJUnitWebConfig
で resourcePath
属性を使用することによってのみ、@WebAppConfiguration
から value
属性をオーバーライドできます。
次の例は、@SpringJUnitWebConfig
アノテーションを使用して構成クラスを指定する方法を示しています。
@SpringJUnitWebConfig(TestConfig.class) (1)
class ConfigurationClassJUnitJupiterSpringWebTests {
// class body...
}
1 | 構成クラスを指定します。 |
@SpringJUnitWebConfig(TestConfig::class) (1)
class ConfigurationClassJUnitJupiterSpringWebTests {
// class body...
}
1 | 構成クラスを指定します。 |
次の例は、@SpringJUnitWebConfig
アノテーションを使用して構成ファイルの場所を指定する方法を示しています。
@SpringJUnitWebConfig(locations = "/test-config.xml") (1)
class XmlJUnitJupiterSpringWebTests {
// class body...
}
1 | 構成ファイルの場所を指定します。 |
@SpringJUnitWebConfig(locations = ["/test-config.xml"]) (1)
class XmlJUnitJupiterSpringWebTests {
// class body...
}
1 | 構成ファイルの場所を指定します。 |
詳細については、コンテキスト管理、および @SpringJUnitWebConfig
(Javadoc) 、@ContextConfiguration
(Javadoc) 、@WebAppConfiguration
(Javadoc) の javadoc を参照してください。
@TestConstructor
@TestConstructor
は、テストのクラスコンストラクターのパラメーターをテストの ApplicationContext
のコンポーネントからオートワイヤーする方法を構成するために使用される型レベルのアノテーションです。
@TestConstructor
がテストクラスに存在しないかメタ存在しない場合、デフォルトのテストコンストラクターオートワイヤーモードが使用されます。デフォルトモードを変更する方法の詳細については、以下のヒントを参照してください。ただし、コンストラクターでの @Autowired
のローカル宣言は、@TestConstructor
とデフォルトモードの両方よりも優先されることに注意してください。
デフォルトのテストコンストラクターオートワイヤーモードの変更 デフォルトのテストコンストラクターの autowire モードは、 Spring Framework 5.3, の時点で、デフォルトモードは JUnit プラットフォーム構成パラメーター (英語) として構成することもできます。
|
Spring Framework 5.2, 以降、@TestConstructor は、JUnit Jupiter で使用する SpringExtension との組み合わせでのみサポートされます。多くの場合、SpringExtension は自動的に登録されます。たとえば、@SpringJUnitConfig や @SpringJUnitWebConfig などのアノテーションや、Spring Boot テストのさまざまなテスト関連のアノテーションを使用する場合などです。 |
@NestedTestConfiguration
@NestedTestConfiguration
は、内部テストクラスの囲んでいるクラス階層内で Spring Test 構成アノテーションがどのように処理されるかを構成するために使用される型レベルのアノテーションです。
@NestedTestConfiguration
がテストクラス、そのスーパー型階層、またはその包含クラス階層に存在しないかメタ存在である場合、デフォルトの包含構成継承モードが使用されます。デフォルトモードを変更する方法の詳細については、以下のヒントを参照してください。
デフォルトの包含構成継承モードの変更 デフォルトの包含構成継承モードは |
Spring TestContext フレームワークは、次のアノテーションの @NestedTestConfiguration
セマンティクスを尊重します。
@NestedTestConfiguration の使用は、通常、JUnitJupiter の @Nested テストクラスと組み合わせた場合にのみ意味があります。ただし、Spring をサポートする他のテストフレームワークと、このアノテーションを利用するネストされたテストクラスが存在する場合があります。 |
例と詳細については、@Nested
テストクラスの構成を参照してください。
@EnabledIf
@EnabledIf
は、アノテーション付きの JUnit Jupiter テストクラスまたはテストメソッドが有効であり、提供された expression
が true
に評価される場合に実行する必要があることを通知するために使用されます。具体的には、式が Boolean.TRUE
または true
に等しい String
(大文字小文字を無視)に評価される場合、テストは有効になります。クラスレベルで適用すると、そのクラス内のすべてのテストメソッドもデフォルトで自動的に有効になります。
式は次のいずれかです。
Spring 式言語(SpEL)式。例:
@EnabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")
Spring
Environment
で使用可能なプロパティのプレースホルダー。例:@EnabledIf("${smoke.tests.enabled}")
テキストリテラル。例:
@EnabledIf("true")
ただし、@EnabledIf("false")
は @Disabled
と同等であり、@EnabledIf("true")
は論理的に無意味であるため、プロパティプレースホルダーの動的解決の結果ではないテキストリテラルはゼロの実用的な値であることに注意してください。
@EnabledIf
をメタアノテーションとして使用して、カスタム合成アノテーションを作成できます。例:次のように、カスタム @EnabledOnMac
アノテーションを作成できます。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@EnabledIf(
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
reason = "Enabled on Mac OS"
)
public @interface EnabledOnMac {}
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@EnabledIf(
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
reason = "Enabled on Mac OS"
)
annotation class EnabledOnMac {}
@DisabledIf
@DisabledIf
は、アノテーション付きの JUnit Jupiter テストクラスまたはテストメソッドが無効になっていることを通知するために使用され、提供された expression
が true
と評価される場合は実行されません。具体的には、式が Boolean.TRUE
または true
に等しい String
(大文字と小文字を区別しない)と評価される場合、テストは無効になります。クラスレベルで適用すると、そのクラス内のすべてのテストメソッドも自動的に無効になります。
式は次のいずれかです。
Spring 式言語(SpEL)式。例:
@DisabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}")
Spring
Environment
で使用可能なプロパティのプレースホルダー。例:@DisabledIf("${smoke.tests.disabled}")
テキストリテラル。例:
@DisabledIf("true")
ただし、@DisabledIf("true")
は @Disabled
と同等であり、@DisabledIf("false")
は論理的に無意味であるため、プロパティプレースホルダーの動的解決の結果ではないテキストリテラルはゼロの実用的な値であることに注意してください。
@DisabledIf
をメタアノテーションとして使用して、カスタム合成アノテーションを作成できます。例:次のように、カスタム @DisabledOnMac
アノテーションを作成できます。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@DisabledIf(
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
reason = "Disabled on Mac OS"
)
public @interface DisabledOnMac {}
@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@DisabledIf(
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
reason = "Disabled on Mac OS"
)
annotation class DisabledOnMac {}
3.4.5. テスト用のメタアノテーションのサポート
ほとんどのテスト関連のアノテーションをメタアノテーションとして使用して、カスタム構成アノテーションを作成し、テストスイート全体の構成の重複を減らすことができます。
次のそれぞれを TestContext フレームワークと組み合わせてメタアノテーションとして使用できます。
@BootstrapWith
@ContextConfiguration
@ContextHierarchy
@ActiveProfiles
@TestPropertySource
@DirtiesContext
@WebAppConfiguration
@TestExecutionListeners
@Transactional
@BeforeTransaction
@AfterTransaction
@Commit
@Rollback
@Sql
@SqlConfig
@SqlMergeMode
@SqlGroup
@Repeat
(JUnit 4 でのみサポート)@Timed
(JUnit 4 でのみサポート)@IfProfileValue
(JUnit 4 でのみサポート)@ProfileValueSourceConfiguration
(JUnit 4 でのみサポート)@SpringJUnitConfig
(JUnit Jupiter でのみサポート)@SpringJUnitWebConfig
(JUnit Jupiter でのみサポート)@TestConstructor
(JUnit Jupiter でのみサポート)@NestedTestConfiguration
(JUnit Jupiter でのみサポート)@EnabledIf
(JUnit Jupiter でのみサポート)@DisabledIf
(JUnit Jupiter でのみサポート)
次の例を考えてみましょう。
@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class OrderRepositoryTests { }
@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class UserRepositoryTests { }
@RunWith(SpringRunner::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
class OrderRepositoryTests { }
@RunWith(SpringRunner::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
class UserRepositoryTests { }
JUnit 4 ベースのテストスイート全体で上記の構成を繰り返していることがわかった場合は、次のように、Spring の共通のテスト構成を集中化するカスタム合成アノテーションを導入することで、重複を減らすことができます。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTestConfig { }
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
annotation class TransactionalDevTestConfig { }
次に、次のように、カスタム @TransactionalDevTestConfig
アノテーションを使用して、個々の JUnit 4 ベースのテストクラスの構成を簡素化できます。
@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class OrderRepositoryTests { }
@RunWith(SpringRunner.class)
@TransactionalDevTestConfig
public class UserRepositoryTests { }
@RunWith(SpringRunner::class)
@TransactionalDevTestConfig
class OrderRepositoryTests
@RunWith(SpringRunner::class)
@TransactionalDevTestConfig
class UserRepositoryTests
JUnit Jupiter を使用するテストを記述する場合、JUnit 5 のアノテーションもメタアノテーションとして使用できるため、コードの重複をさらに減らすことができます。次の例を考えてみましょう。
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
class OrderRepositoryTests { }
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
class UserRepositoryTests { }
@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
class OrderRepositoryTests { }
@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
class UserRepositoryTests { }
JUnit Jupiter ベースのテストスイート全体で上記の構成を繰り返していることがわかった場合、次のように、Spring と JUnit Jupiter の共通のテスト構成を集中化するカスタム構成アノテーションを導入することで、重複を減らすことができます。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTestConfig { }
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-data-access-config.xml")
@ActiveProfiles("dev")
@Transactional
annotation class TransactionalDevTestConfig { }
次に、次のように、カスタム @TransactionalDevTestConfig
アノテーションを使用して、個々の JUnit Jupiter ベースのテストクラスの構成を簡素化できます。
@TransactionalDevTestConfig
class OrderRepositoryTests { }
@TransactionalDevTestConfig
class UserRepositoryTests { }
@TransactionalDevTestConfig
class OrderRepositoryTests { }
@TransactionalDevTestConfig
class UserRepositoryTests { }
JUnit Jupiter は @Test
、@RepeatedTest
、ParameterizedTest
などのメタアノテーションとしての使用をサポートしているため、テストメソッドレベルでカスタム合成アノテーションを作成することもできます。例:JUnit Jupiter の @Test
アノテーションと @Tag
アノテーションを Spring の @Transactional
アノテーションと組み合わせる合成アノテーションを作成する場合、次のように @TransactionalIntegrationTest
アノテーションを作成できます。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Transactional
@Tag("integration-test") // org.junit.jupiter.api.Tag
@Test // org.junit.jupiter.api.Test
public @interface TransactionalIntegrationTest { }
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@Transactional
@Tag("integration-test") // org.junit.jupiter.api.Tag
@Test // org.junit.jupiter.api.Test
annotation class TransactionalIntegrationTest { }
次に、カスタム @TransactionalIntegrationTest
アノテーションを使用して、次のように、個々の JUnit Jupiter ベースのテストメソッドの構成を簡素化できます。
@TransactionalIntegrationTest
void saveOrder() { }
@TransactionalIntegrationTest
void deleteOrder() { }
@TransactionalIntegrationTest
fun saveOrder() { }
@TransactionalIntegrationTest
fun deleteOrder() { }
詳細については、Spring アノテーションプログラミングモデル : GitHub (英語) wiki ページを参照してください。
3.5. Spring TestContext フレームワーク
Spring TestContext フレームワーク(org.springframework.test.context
パッケージにあります)は、使用中のテストフレームワークにとらわれない、汎用のアノテーション駆動型ユニットおよび統合テストのサポートを提供します。また、TestContext フレームワークは、構成よりも規約を非常に重視しており、アノテーションベースの構成でオーバーライドできる妥当なデフォルトを備えています。
一般的なテストインフラストラクチャに加えて、TestContext フレームワークは、JUnit 4、JUnit Jupiter(別名 JUnit 5)、および TestNG の明示的なサポートを提供します。JUnit 4 および TestNG の場合、Spring は abstract
サポートクラスを提供します。さらに、Spring は、JUnit 4 用のカスタム JUnit Runner
とカスタム JUnit Rules
、および JUnit Jupiter 用のカスタム Extension
を提供し、いわゆる POJO テストクラスを作成できるようにします。POJO テストクラスは、abstract
サポートクラスなど、特定のクラス階層を継承する必要はありません。
次のセクションでは、TestContext フレームワークの内部の概要を説明します。フレームワークの使用のみに関心があり、独自のカスタムリスナーまたはカスタムローダーで拡張することに興味がない場合は、構成(コンテキスト管理、依存性注入、トランザクション管理)、サポートクラス、およびアノテーションサポートに直接アクセスしてください。セクション。
3.5.1. 主要な抽象化
フレームワークのコアは、TestContextManager
クラス、TestContext
、TestExecutionListener
、SmartContextLoader
インターフェースで構成されています。TestContextManager
は、テストクラスごとに作成されます(たとえば、JUnit Jupiter の単一のテストクラス内のすべてのテストメソッドの実行用)。TestContextManager
は、現在のテストのコンテキストを保持する TestContext
を管理します。また、TestContextManager
は、テストの進行に応じて TestContext
の状態を更新し、TestExecutionListener
実装に委譲します。TestExecutionListener
実装は、依存関係の注入、トランザクションの管理などを提供することにより、実際のテスト実行を計測します。SmartContextLoader
は、所定のテストクラスの ApplicationContext
をロードするロールを果たします。さまざまな実装の詳細と例については、javadoc および Spring Test スイートを参照してください。
TestContext
TestContext
は、テストが実行されるコンテキスト(使用中の実際のテストフレームワークに依存しない)をカプセル化し、それが担当するテストインスタンスにコンテキスト管理とキャッシュサポートを提供します。TestContext
は、リクエストされた場合に SmartContextLoader
に委譲して ApplicationContext
をロードします。
TestContextManager
TestContextManager
は、Spring TestContext フレームワークへのメインエントリポイントであり、単一の TestContext
を管理し、明確に定義されたテスト実行ポイントで各登録済み TestExecutionListener
にイベントを通知します。
特定のテストフレームワークの「before before」または「before before」メソッドの前。
テストインスタンスの後処理。
特定のテストフレームワークの「前」または「各前」メソッドの前。
テストメソッドの実行直前、ただしテストのセットアップ後。
テストメソッドの実行直後、テストの分解前。
特定のテストフレームワークの「後」または「各後」メソッドの後。
特定のテストフレームワークの「クラスの後」または「すべての後」のメソッドの後。
TestExecutionListener
TestExecutionListener
は、リスナーが登録されている TestContextManager
によって発行されたテスト実行イベントに反応するための API を定義します。TestExecutionListener
設定を参照してください。
コンテキストローダー
ContextLoader
は、Spring TestContext フレームワークによって管理される統合テスト用に ApplicationContext
をロードするための戦略インターフェースです。このインターフェースの代わりに SmartContextLoader
を実装して、コンポーネントクラス、アクティブな Bean 定義プロファイル、テストプロパティソース、コンテキスト階層、WebApplicationContext
サポートをサポートする必要があります。
SmartContextLoader
は、オリジナルの最小 ContextLoader
SPI に取って代わる ContextLoader
インターフェースの拡張です。具体的には、SmartContextLoader
は、リソースの場所、コンポーネントクラス、またはコンテキスト初期化子の処理を選択できます。さらに、SmartContextLoader
は、アクティブな Bean 定義プロファイルを設定し、ロードするコンテキストでプロパティソースをテストできます。
Spring は、次の実装を提供します。
DelegatingSmartContextLoader
: 2 つのデフォルトローダーの 1 つ。テストクラスに対して宣言された構成、またはデフォルトの場所またはデフォルトの構成クラスの存在に応じて、内部でAnnotationConfigContextLoader
、GenericXmlContextLoader
、またはGenericGroovyXmlContextLoader
に委譲します。Groovy サポートは、Groovy がクラスパス上にある場合にのみ有効になります。WebDelegatingSmartContextLoader
: 2 つのデフォルトローダーの 1 つ。テストクラスに対して宣言された構成、またはデフォルトの場所またはデフォルトの構成クラスの存在に応じて、内部でAnnotationConfigWebContextLoader
、GenericXmlWebContextLoader
、またはGenericGroovyXmlWebContextLoader
に委譲します。WebContextLoader
は、@WebAppConfiguration
がテストクラスに存在する場合にのみ使用されます。Groovy サポートは、Groovy がクラスパス上にある場合にのみ有効になります。AnnotationConfigContextLoader
: コンポーネントクラスから標準ApplicationContext
をロードします。AnnotationConfigWebContextLoader
: コンポーネントクラスからWebApplicationContext
をロードします。GenericGroovyXmlContextLoader
: Groovy スクリプトまたは XML 構成ファイルのいずれかであるリソースの場所から標準ApplicationContext
をロードします。GenericGroovyXmlWebContextLoader
: Groovy スクリプトまたは XML 構成ファイルのいずれかであるリソースの場所からWebApplicationContext
をロードします。GenericXmlContextLoader
: XML リソースの場所から標準ApplicationContext
をロードします。GenericXmlWebContextLoader
: XML リソースの場所からWebApplicationContext
をロードします。
3.5.2. TestContext フレームワークのブートストラップ
Spring TestContext フレームワークの内部のデフォルト構成は、すべての一般的なユースケースで十分です。ただし、開発チームまたはサードパーティのフレームワークがデフォルトの ContextLoader
を変更したり、カスタム TestContext
または ContextCache
を実装したり、ContextCustomizerFactory
および TestExecutionListener
実装のデフォルトセットを拡張したりする場合があります。TestContext フレームワークの動作をこのように低レベルで制御するために、Spring はブートストラップ戦略を提供します。
TestContextBootstrapper
は、TestContext フレームワークをブートストラップするための SPI を定義します。TestContextBootstrapper
は、現在のテストの TestExecutionListener
実装をロードし、管理する TestContext
を構築するために、TestContextManager
によって使用されます。@BootstrapWith
を直接またはメタアノテーションとして使用して、テストクラス(またはテストクラス階層)のカスタムブートストラップ戦略を構成できます。@BootstrapWith
を使用してブートストラップが明示的に構成されていない場合、@WebAppConfiguration
の存在に応じて、DefaultTestContextBootstrapper
または WebTestContextBootstrapper
のいずれかが使用されます。
TestContextBootstrapper
SPI は将来(新しい要件に対応するため)変更される可能性があるため、実装者はこのインターフェースを直接実装せず、代わりに AbstractTestContextBootstrapper
またはその具象サブクラスの 1 つを継承することを強くお勧めします。
3.5.3. TestExecutionListener
設定
Spring は、デフォルトで正確に次の順序で登録される次の TestExecutionListener
実装を提供します。
ServletTestExecutionListener
:WebApplicationContext
のサーブレット API モックを構成します。DirtiesContextBeforeModesTestExecutionListener
: 「前」モードの@DirtiesContext
アノテーションを処理します。ApplicationEventsTestExecutionListener
:ApplicationEvents
をサポートします。DependencyInjectionTestExecutionListener
: テストインスタンスに依存性注入を提供します。DirtiesContextTestExecutionListener
: 「after」モードの@DirtiesContext
アノテーションを処理します。TransactionalTestExecutionListener
: トランザクションテストの実行にデフォルトのロールバックセマンティクスを提供します。SqlScriptsTestExecutionListener
:@Sql
アノテーションを使用して構成された SQL スクリプトを実行します。EventPublishingTestExecutionListener
: テストの実行イベントをテストのApplicationContext
に公開します(テスト実行イベントを参照)。
TestExecutionListener
実装の登録
@TestExecutionListeners
アノテーションを使用して、テストクラスとそのサブクラスの TestExecutionListener
実装を登録できます。詳細と例については、アノテーションサポートと @TestExecutionListeners
(Javadoc) の javadoc を参照してください。
デフォルトの TestExecutionListener
実装の自動検出
@TestExecutionListeners
を使用して TestExecutionListener
実装を登録することは、限られたテストシナリオで使用されるカスタムリスナーに適しています。ただし、テストスイート全体でカスタムリスナーを使用する必要がある場合は、面倒になります。この課題は、SpringFactoriesLoader
メカニズムによるデフォルトの TestExecutionListener
実装の自動検出のサポートにより解決されています。
具体的には、spring-test
モジュールは、META-INF/spring.factories
プロパティファイルの org.springframework.test.context.TestExecutionListener
キーで、すべてのコアデフォルト TestExecutionListener
実装を宣言します。サードパーティのフレームワークと開発者は、独自の META-INF/spring.factories
プロパティファイルを介して、同じ方法で独自の TestExecutionListener
実装をデフォルトリスナーのリストに提供できます。
TestExecutionListener
実装のオーダー
TestContext フレームワークが前述の SpringFactoriesLoader
メカニズムを介してデフォルト TestExecutionListener
実装を発見すると、Spring の AnnotationAwareOrderComparator
を使用してインスタンス化されたリスナーがソートされ、Spring の Ordered
インターフェースと @Order
アノテーションが順序付けされます。AbstractTestExecutionListener
および Spring が提供するすべてのデフォルト TestExecutionListener
実装は、適切な値で Ordered
を実装します。サードパーティのフレームワークと開発者は、Ordered
を実装するか @Order
を宣言することにより、デフォルトの TestExecutionListener
実装が適切な順序で登録されていることを確認する必要があります。各コアリスナーに割り当てられる値の詳細については、コアのデフォルト TestExecutionListener
実装の getOrder()
メソッドの javadoc を参照してください。
TestExecutionListener
実装のマージ
カスタム TestExecutionListener
が @TestExecutionListeners
を介して登録されている場合、デフォルトのリスナーは登録されません。最も一般的なテストシナリオでは、これにより、開発者は、カスタムリスナーに加えて、すべてのデフォルトリスナーを手動で宣言する必要があります。次のリストは、このスタイルの構成を示しています。
@ContextConfiguration
@TestExecutionListeners({
MyCustomTestExecutionListener.class,
ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class
})
class MyTest {
// class body...
}
@ContextConfiguration
@TestExecutionListeners(
MyCustomTestExecutionListener::class,
ServletTestExecutionListener::class,
DirtiesContextBeforeModesTestExecutionListener::class,
DependencyInjectionTestExecutionListener::class,
DirtiesContextTestExecutionListener::class,
TransactionalTestExecutionListener::class,
SqlScriptsTestExecutionListener::class
)
class MyTest {
// class body...
}
このアプローチの課題は、開発者がデフォルトで登録されているリスナーを正確に知っている必要があることです。さらに、デフォルトリスナーのセットはリリースごとに変更できます。たとえば、SqlScriptsTestExecutionListener
は Spring Framework 4.1, で導入され、DirtiesContextBeforeModesTestExecutionListener
は Spring Framework 4.2. で導入されます。さらに、Spring Boot や Spring Security などのサードパーティフレームワークは、前述の自動検出を使用して独自のデフォルト TestExecutionListener
実装を登録しますメカニズム。
すべてのデフォルトリスナーを認識して再宣言する必要を回避するために、@TestExecutionListeners
の mergeMode
属性を MergeMode.MERGE_WITH_DEFAULTS
に設定できます。MERGE_WITH_DEFAULTS
は、ローカルで宣言されたリスナーをデフォルトのリスナーとマージする必要があることを示します。マージアルゴリズムにより、TestExecutionListener
実装のオーダーに従って、重複がリストから削除され、マージされたリスナーの結果セットが AnnotationAwareOrderComparator
のセマンティクスに従ってソートされます。リスナーが Ordered
を実装するか、@Order
のアノテーションが付けられている場合、リスナーがデフォルトとマージされる位置に影響を与える可能性があります。それ以外の場合、ローカルで宣言されたリスナーは、マージ時にデフォルトのリスナーのリストに追加されます。
例:前の例の MyCustomTestExecutionListener
クラスが order
値(たとえば 500
)を ServletTestExecutionListener
(たまたま 1000
)の順序よりも小さくなるように構成した場合、MyCustomTestExecutionListener
は自動的にデフォルトのリストにマージされます。ServletTestExecutionListener
の前面、および前の例は次のように置き換えることができます。
@ContextConfiguration
@TestExecutionListeners(
listeners = MyCustomTestExecutionListener.class,
mergeMode = MERGE_WITH_DEFAULTS
)
class MyTest {
// class body...
}
@ContextConfiguration
@TestExecutionListeners(
listeners = [MyCustomTestExecutionListener::class],
mergeMode = MERGE_WITH_DEFAULTS
)
class MyTest {
// class body...
}
3.5.4. アプリケーションイベント
Spring Framework 5.3.3, 以降、TestContext フレームワークは、ApplicationContext
で公開されたアプリケーションイベントの記録をサポートしているため、テスト内でこれらのイベントに対してアサーションを実行できます。単一のテストの実行中に公開されたすべてのイベントは、ApplicationEvents
API を介して利用可能になります。これにより、イベントを java.util.Stream
として処理できます。
テストで ApplicationEvents
を使用するには、次のようにします。
テストクラスに
@RecordApplicationEvents
のアノテーションまたはメタアノテーションが付けられていることを確認してください。ApplicationEventsTestExecutionListener
が登録されていることを確認してください。ただし、ApplicationEventsTestExecutionListener
はデフォルトで登録されており、デフォルトのリスナーを含まない@TestExecutionListeners
を介したカスタム構成がある場合にのみ、手動で登録する必要があることに注意してください。タイプ
ApplicationEvents
のフィールドに@Autowired
アノテーションを付け、そのApplicationEvents
のインスタンスをテストメソッドとライフサイクルメソッド(JUnit Jupiter の@BeforeEach
メソッドや@AfterEach
メソッドなど)で使用します。JUnit Jupiter 用 SpringExtension を使用する場合、テストクラスの
@Autowired
フィールドの代わりに、テストまたはライフサイクルメソッドでタイプApplicationEvents
のメソッドパラメーターを宣言できます。
次のテストクラスは、JUnit Jupiter の SpringExtension
と AssertJ (英語) を使用して、Spring 管理コンポーネントのメソッドの呼び出し中に公開されたアプリケーションイベントのタイプをアサートします。
@SpringJUnitConfig(/* ... */)
@RecordApplicationEvents (1)
class OrderServiceTests {
@Autowired
OrderService orderService;
@Autowired
ApplicationEvents events; (2)
@Test
void submitOrder() {
// Invoke method in OrderService that publishes an event
orderService.submitOrder(new Order(/* ... */));
// Verify that an OrderSubmitted event was published
int numEvents = events.stream(OrderSubmitted.class).count(); (3)
assertThat(numEvents).isEqualTo(1);
}
}
1 | テストクラスに @RecordApplicationEvents アノテーションを付けます。 |
2 | 現在のテストに ApplicationEvents インスタンスを注入します。 |
3 | ApplicationEvents API を使用して、公開された OrderSubmitted イベントの数をカウントします。 |
@SpringJUnitConfig(/* ... */)
@RecordApplicationEvents (1)
class OrderServiceTests {
@Autowired
lateinit var orderService: OrderService
@Autowired
lateinit var events: ApplicationEvents (2)
@Test
fun submitOrder() {
// Invoke method in OrderService that publishes an event
orderService.submitOrder(Order(/* ... */))
// Verify that an OrderSubmitted event was published
val numEvents = events.stream(OrderSubmitted::class).count() (3)
assertThat(numEvents).isEqualTo(1)
}
}
1 | テストクラスに @RecordApplicationEvents アノテーションを付けます。 |
2 | 現在のテストに ApplicationEvents インスタンスを注入します。 |
3 | ApplicationEvents API を使用して、公開された OrderSubmitted イベントの数をカウントします。 |
ApplicationEvents
API の詳細については、ApplicationEvents
javadoc を参照してください。
3.5.5. テスト実行イベント
Spring Framework 5.2 で導入された EventPublishingTestExecutionListener
は、カスタム TestExecutionListener
を実装するための代替アプローチを提供します。テストの ApplicationContext
のコンポーネントは、EventPublishingTestExecutionListener
によって発行された次のイベントをリッスンできます。各イベントは、TestExecutionListener
API のメソッドに対応しています。
BeforeTestClassEvent
PrepareTestInstanceEvent
BeforeTestMethodEvent
BeforeTestExecutionEvent
AfterTestExecutionEvent
AfterTestMethodEvent
AfterTestClassEvent
これらのイベントは、ApplicationContext が既にロードされている場合にのみ公開されます。 |
これらのイベントは、モック Bean のリセットやテスト実行のトレースなど、さまざまな理由で消費される場合があります。カスタム TestExecutionListener
を実装するのではなく、テスト実行イベントを消費する利点の 1 つは、テスト ApplicationContext
に登録された Spring Bean がテスト実行イベントを消費できること、およびそのような Bean が依存性注入および ApplicationContext
の他の機能から直接恩恵を受ける可能性があることです。対照的に、TestExecutionListener
は ApplicationContext
の Bean ではありません。
テスト実行イベントをリッスンするために、Spring Bean は org.springframework.context.ApplicationListener
インターフェースの実装を選択できます。または、リスナーメソッドに @EventListener
アノテーションを付けて、上記の特定のイベントタイプの 1 つをリッスンするように構成できます(アノテーションベースのイベントリスナーを参照)。このアプローチの人気により、Spring は次の専用 @EventListener
アノテーションを提供して、テスト実行イベントリスナーの登録を簡素化します。これらのアノテーションは、org.springframework.test.context.event.annotation
パッケージにあります。
@BeforeTestClass
@PrepareTestInstance
@BeforeTestMethod
@BeforeTestExecution
@AfterTestExecution
@AfterTestMethod
@AfterTestClass
例外処理
デフォルトでは、テスト実行イベントリスナーがイベントの消費中に例外をスローすると、その例外は使用中の基礎となるテストフレームワーク(JUnit や TestNG など)に伝播します。例: BeforeTestMethodEvent
の消費により例外が発生した場合、対応するテストメソッドは例外の結果として失敗します。対照的に、非同期のテスト実行イベントリスナーが例外をスローする場合、例外は基礎となるテストフレームワークに伝播しません。非同期例外処理の詳細については、@EventListener
のクラスレベルの javadoc を参照してください。
非同期リスナー
特定のテスト実行イベントリスナーでイベントを非同期的に処理する場合は、Spring の通常の @Async
サポートを使用できます。詳細については、@EventListener
のクラスレベルの javadoc を参照してください。
3.5.6. コンテキスト管理
各 TestContext
は、担当するテストインスタンスのコンテキスト管理とキャッシュサポートを提供します。テストインスタンスは、構成された ApplicationContext
へのアクセスを自動的に受け取りません。ただし、テストクラスが ApplicationContextAware
インターフェースを実装する場合、ApplicationContext
への参照がテストインスタンスに提供されます。AbstractJUnit4SpringContextTests
および AbstractTestNGSpringContextTests
は ApplicationContextAware
を実装しているため、ApplicationContext
へのアクセスを自動的に提供することに注意してください。
@Autowired ApplicationContext
Java
Kotlin
同様に、テストが Java
Kotlin
|
TestContext フレームワークを使用するテストクラスは、特定のクラスを継承したり、特定のインターフェースを実装してアプリケーションコンテキストを構成したりする必要はありません。代わりに、構成はクラスレベルで @ContextConfiguration
アノテーションを宣言することで実現されます。テストクラスがアプリケーションコンテキストリソースの場所またはコンポーネントクラスを明示的に宣言しない場合、構成された ContextLoader
は、デフォルトの場所またはデフォルトの構成クラスからコンテキストをロードする方法を決定します。コンテキストリソースの場所とコンポーネントクラスに加えて、アプリケーションコンテキストは、アプリケーションコンテキスト初期化子を通じて構成することもできます。
以下のセクションでは、Spring の @ContextConfiguration
アノテーションを使用して、XML 構成ファイル、Groovy スクリプト、コンポーネントクラス(通常 @Configuration
クラス)、またはコンテキスト初期化子を使用して、テスト ApplicationContext
を構成する方法について説明します。または、高度なユースケース用に独自のカスタム SmartContextLoader
を実装および構成できます。
XML リソースを使用したコンテキスト設定
XML 構成ファイルを使用して ApplicationContext
をテスト用にロードするには、テストクラスに @ContextConfiguration
のアノテーションを付け、XML 構成メタデータのリソースの場所を含む配列で locations
属性を構成します。プレーンパスまたは相対パス(たとえば、context.xml
)は、テストクラスが定義されているパッケージに相対的なクラスパスリソースとして扱われます。スラッシュで始まるパスは、絶対クラスパスの場所として扱われます(たとえば、/org/example/config.xml
)。リソース URL を表すパス(つまり、接頭辞 classpath:
、file:
、http:
など)がそのまま使用されます。
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "/app-config.xml" and
// "/test-config.xml" in the root of the classpath
@ContextConfiguration(locations={"/app-config.xml", "/test-config.xml"}) (1)
class MyTest {
// class body...
}
1 | ロケーション属性を XML ファイルのリストに設定します。 |
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from "/app-config.xml" and
// "/test-config.xml" in the root of the classpath
@ContextConfiguration("/app-config.xml", "/test-config.xml") (1)
class MyTest {
// class body...
}
1 | ロケーション属性を XML ファイルのリストに設定します。 |
@ContextConfiguration
は、標準 Java value
属性を介して locations
属性のエイリアスをサポートします。@ContextConfiguration
で追加の属性を宣言する必要がない場合は、次の例に示す短縮形を使用して、locations
属性名の宣言を省略し、リソースの場所を宣言できます。
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-config.xml"}) (1)
class MyTest {
// class body...
}
1 | location 属性を使用せずに XML ファイルを指定します。 |
@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-config.xml") (1)
class MyTest {
// class body...
}
1 | location 属性を使用せずに XML ファイルを指定します。 |
@ContextConfiguration
アノテーションから locations
属性と value
属性の両方を省略すると、TestContext フレームワークはデフォルトの XML リソースの場所を検出しようとします。具体的には、GenericXmlContextLoader
および GenericXmlWebContextLoader
は、テストクラスの名前に基づいてデフォルトの場所を検出します。クラスの名前が com.example.MyTest
、GenericXmlContextLoader
の場合、"classpath:com/example/MyTest-context.xml"
からアプリケーションコンテキストをロードします。次の例は、その方法を示しています。
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTest-context.xml"
@ContextConfiguration (1)
class MyTest {
// class body...
}
1 | デフォルトの場所から構成をロードしています。 |
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTest-context.xml"
@ContextConfiguration (1)
class MyTest {
// class body...
}
1 | デフォルトの場所から構成をロードしています。 |
Groovy スクリプトを使用したコンテキスト設定
Groovy Bean 定義 DSL を使用する Groovy スクリプトを使用して、テスト用に ApplicationContext
をロードするには、@ContextConfiguration
でテストクラスにアノテーションを付け、Groovy スクリプトのリソースの場所を含む配列で locations
または value
属性を構成します。Groovy スクリプトのリソースルックアップセマンティクスは、XML 構成ファイルについて説明したものと同じです。
Groovy スクリプトサポートの有効化 Groovy がクラスパス上にある場合、Groovy スクリプトを使用して Spring に ApplicationContext をロードするためのサポート TestContext フレームワークは自動的に有効になります。 |
次の例は、Groovy 構成ファイルを指定する方法を示しています。
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "/AppConfig.groovy" and
// "/TestConfig.groovy" in the root of the classpath
@ContextConfiguration({"/AppConfig.groovy", "/TestConfig.Groovy"}) (1)
class MyTest {
// class body...
}
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from "/AppConfig.groovy" and
// "/TestConfig.groovy" in the root of the classpath
@ContextConfiguration("/AppConfig.groovy", "/TestConfig.Groovy") (1)
class MyTest {
// class body...
}
1 | Groovy 構成ファイルの場所を指定します。 |
@ContextConfiguration
アノテーションから locations
属性と value
属性の両方を省略すると、TestContext フレームワークはデフォルトの Groovy スクリプトを検出しようとします。具体的には、GenericGroovyXmlContextLoader
および GenericGroovyXmlWebContextLoader
は、テストクラスの名前に基づいてデフォルトの場所を検出します。クラスの名前が com.example.MyTest
の場合、Groovy コンテキストローダーは "classpath:com/example/MyTestContext.groovy"
からアプリケーションコンテキストをロードします。次の例は、デフォルトの使用方法を示しています。
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTestContext.groovy"
@ContextConfiguration (1)
class MyTest {
// class body...
}
1 | デフォルトの場所から構成をロードしています。 |
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTestContext.groovy"
@ContextConfiguration (1)
class MyTest {
// class body...
}
1 | デフォルトの場所から構成をロードしています。 |
XML 構成と Groovy スクリプトを同時に宣言する
次のリストは、統合テストで両方を組み合わせる方法を示しています。 Java
Kotlin
|
コンポーネントクラスを使用したコンテキスト設定
コンポーネントクラス(Java ベースのコンテナー構成を参照)を使用して ApplicationContext
をテスト用にロードするには、テストクラスに @ContextConfiguration
のアノテーションを付け、コンポーネントクラスへの参照を含む配列で classes
属性を構成できます。次の例は、その方法を示しています。
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from AppConfig and TestConfig
@ContextConfiguration(classes = {AppConfig.class, TestConfig.class}) (1)
class MyTest {
// class body...
}
1 | コンポーネントクラスの指定。 |
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from AppConfig and TestConfig
@ContextConfiguration(classes = [AppConfig::class, TestConfig::class]) (1)
class MyTest {
// class body...
}
1 | コンポーネントクラスの指定。 |
コンポーネントクラス 「コンポーネントクラス」という用語は、次のいずれかを指します。
|
@ContextConfiguration
アノテーションから classes
属性を省略すると、TestContext フレームワークはデフォルトの構成クラスの存在を検出しようとします。具体的には、AnnotationConfigContextLoader
および AnnotationConfigWebContextLoader
は、@Configuration
(Javadoc) javadoc で指定されているように、構成クラス実装の要件を満たすテストクラスの static
ネストクラスをすべて検出します。構成クラスの名前は任意です。さらに、テストクラスには、必要に応じて複数の static
ネストされた構成クラスを含めることができます。次の例では、OrderServiceTest
クラスは、テストクラスの ApplicationContext
をロードするために自動的に使用される Config
という名前の static
ネストされた構成クラスを宣言します。
@SpringJUnitConfig (1)
// ApplicationContext will be loaded from the
// static nested Config class
class OrderServiceTest {
@Configuration
static class Config {
// this bean will be injected into the OrderServiceTest class
@Bean
OrderService orderService() {
OrderService orderService = new OrderServiceImpl();
// set properties, etc.
return orderService;
}
}
@Autowired
OrderService orderService;
@Test
void testOrderService() {
// test the orderService
}
}
1 | ネストされた Config クラスから構成情報をロードします。 |
@SpringJUnitConfig (1)
// ApplicationContext will be loaded from the nested Config class
class OrderServiceTest {
@Autowired
lateinit var orderService: OrderService
@Configuration
class Config {
// this bean will be injected into the OrderServiceTest class
@Bean
fun orderService(): OrderService {
// set properties, etc.
return OrderServiceImpl()
}
}
@Test
fun testOrderService() {
// test the orderService
}
}
1 | ネストされた Config クラスから構成情報をロードします。 |
XML、Groovy スクリプト、およびコンポーネントクラスの混合
テスト用に ApplicationContext
を構成するには、XML 構成ファイル、Groovy スクリプト、およびコンポーネントクラス(通常は @Configuration
クラス)を混在させることが望ましい場合があります。例:本番環境で XML 構成を使用する場合、@Configuration
クラスを使用して、テスト用に特定の Spring 管理コンポーネントを構成したり、その逆を行うことができます。
さらに、一部のサードパーティフレームワーク(Spring Boot など)は、異なる種類のリソース(たとえば、XML 構成ファイル、Groovy スクリプト、@Configuration
クラス)から ApplicationContext
を同時に読み込むためのファーストクラスのサポートを提供します。歴史的に、Spring Framework は標準デプロイに対してこれをサポートしていませんでした。その結果、Spring Framework が spring-test
モジュールで提供する SmartContextLoader
実装のほとんどは、各テストコンテキストに対して 1 つのリソースタイプのみをサポートします。ただし、これは両方を使用できないという意味ではありません。一般規則の 1 つの例外は、GenericGroovyXmlContextLoader
と GenericGroovyXmlWebContextLoader
が XML 構成ファイルと Groovy スクリプトの両方を同時にサポートすることです。さらに、サードパーティのフレームワークは、locations
と classes
から @ContextConfiguration
の両方の宣言をサポートすることを選択できます。また、TestContext フレームワークの標準テストサポートでは、次のオプションがあります。
リソースの場所(XML や Groovy など)と @Configuration
クラスを使用してテストを構成する場合は、一方をエントリポイントとして選択し、もう一方を含めるかインポートする必要があります。例:XML または Groovy スクリプトでは、コンポーネントスキャンを使用するか、通常の Spring Bean として定義することにより、@Configuration
クラスを含めることができます。一方、@Configuration
クラスでは、@ImportResource
を使用して XML 構成ファイルまたは Groovy スクリプトをインポートできます。この動作は、本番環境でアプリケーションを構成する方法と意味的に同等です。本番構成では、XML または Groovy リソースの場所のセット、または本番 ApplicationContext
がロードされる @Configuration
クラスのセットのいずれかを定義しますが、他のタイプの構成を含めたりインポートしたりする自由。
コンテキスト初期化子を使用したコンテキスト構成
コンテキスト初期化子を使用してテスト用に ApplicationContext
を構成するには、@ContextConfiguration
でテストクラスにアノテーションを付け、ApplicationContextInitializer
を実装するクラスへの参照を含む配列で initializers
属性を構成します。宣言されたコンテキスト初期化子は、テスト用にロードされる ConfigurableApplicationContext
を初期化するために使用されます。宣言された各イニシャライザによってサポートされる具体的な ConfigurableApplicationContext
タイプは、使用中の SmartContextLoader
によって作成された ApplicationContext
のタイプ(通常は GenericApplicationContext
)と互換性がある必要があることに注意してください。さらに、初期化子が呼び出される順序は、Spring の Ordered
インターフェースを実装するか、Spring の @Order
アノテーションまたは標準 @Priority
アノテーションが付けられているかによって異なります。次の例は、初期化子の使用方法を示しています。
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from TestConfig
// and initialized by TestAppCtxInitializer
@ContextConfiguration(
classes = TestConfig.class,
initializers = TestAppCtxInitializer.class) (1)
class MyTest {
// class body...
}
1 | 構成クラスと初期化子を使用して構成を指定します。 |
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from TestConfig
// and initialized by TestAppCtxInitializer
@ContextConfiguration(
classes = [TestConfig::class],
initializers = [TestAppCtxInitializer::class]) (1)
class MyTest {
// class body...
}
1 | 構成クラスと初期化子を使用して構成を指定します。 |
また、XML 構成ファイル、Groovy スクリプト、または @ContextConfiguration
のコンポーネントクラスの宣言を完全に省略し、代わりに ApplicationContextInitializer
クラスのみを宣言することができます。ApplicationContextInitializer
クラスは、XML ファイルから Bean 定義をプログラムでロードしたり、構成クラス。次の例は、その方法を示しています。
@ExtendWith(SpringExtension.class)
// ApplicationContext will be initialized by EntireAppInitializer
// which presumably registers beans in the context
@ContextConfiguration(initializers = EntireAppInitializer.class) (1)
class MyTest {
// class body...
}
1 | 初期化子のみを使用して構成を指定します。 |
@ExtendWith(SpringExtension::class)
// ApplicationContext will be initialized by EntireAppInitializer
// which presumably registers beans in the context
@ContextConfiguration(initializers = [EntireAppInitializer::class]) (1)
class MyTest {
// class body...
}
1 | 初期化子のみを使用して構成を指定します。 |
コンテキスト設定の継承
@ContextConfiguration
は、ブール型 inheritLocations
および inheritInitializers
属性をサポートします。これらの属性は、リソースの場所またはコンポーネントクラス、およびスーパークラスによって宣言されたコンテキスト初期化子を継承する必要があるかどうかを示します。両方のフラグのデフォルト値は true
です。つまり、テストクラスは、リソースの場所またはコンポーネントクラス、およびスーパークラスによって宣言されたコンテキスト初期化子を継承します。具体的には、テストクラスのリソースの場所またはコンポーネントクラスは、スーパークラスによって宣言されたリソースの場所またはアノテーション付きクラスのリストに追加されます。同様に、特定のテストクラスの初期化子は、テストスーパークラスによって定義された初期化子のセットに追加されます。サブクラスには、リソースの場所、コンポーネントクラス、またはコンテキスト初期化子を継承するオプションがあります。
@ContextConfiguration
の inheritLocations
または inheritInitializers
属性が false
に設定されている場合、テストクラスシャドウのリソースの場所またはコンポーネントクラスとコンテキスト初期化子はそれぞれ、スーパークラスによって定義された構成を効果的に置き換えます。
Spring Framework 5.3, 以降、テスト構成は、囲んでいるクラスから継承することもできます。詳細については、@Nested テストクラスの構成を参照してください。 |
XML リソースの場所を使用する次の例では、ExtendedTest
の ApplicationContext
が base-config.xml
および extended-config.xml
からこの順序でロードされます。extended-config.xml
で定義された Bean は、base-config.xml
で定義された Bean をオーバーライド(つまり、置換)できます。次の例は、あるクラスが別のクラスを継承し、独自の構成ファイルとスーパークラスの構成ファイルの両方を使用する方法を示しています。
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "/base-config.xml"
// in the root of the classpath
@ContextConfiguration("/base-config.xml") (1)
class BaseTest {
// class body...
}
// ApplicationContext will be loaded from "/base-config.xml" and
// "/extended-config.xml" in the root of the classpath
@ContextConfiguration("/extended-config.xml") (2)
class ExtendedTest extends BaseTest {
// class body...
}
1 | スーパークラスで定義された構成ファイル。 |
2 | サブクラスで定義された構成ファイル。 |
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from "/base-config.xml"
// in the root of the classpath
@ContextConfiguration("/base-config.xml") (1)
open class BaseTest {
// class body...
}
// ApplicationContext will be loaded from "/base-config.xml" and
// "/extended-config.xml" in the root of the classpath
@ContextConfiguration("/extended-config.xml") (2)
class ExtendedTest : BaseTest() {
// class body...
}
1 | スーパークラスで定義された構成ファイル。 |
2 | サブクラスで定義された構成ファイル。 |
同様に、コンポーネントクラスを使用する次の例では、ExtendedTest
の ApplicationContext
が BaseConfig
クラスと ExtendedConfig
クラスからこの順序でロードされます。ExtendedConfig
で定義された Bean は、BaseConfig
で定義された Bean をオーバーライド(つまり、置換)できます。次の例は、あるクラスが別のクラスを継承し、独自の構成クラスとスーパークラスの構成クラスの両方を使用する方法を示しています。
// ApplicationContext will be loaded from BaseConfig
@SpringJUnitConfig(BaseConfig.class) (1)
class BaseTest {
// class body...
}
// ApplicationContext will be loaded from BaseConfig and ExtendedConfig
@SpringJUnitConfig(ExtendedConfig.class) (2)
class ExtendedTest extends BaseTest {
// class body...
}
1 | スーパークラスで定義された構成クラス。 |
2 | サブクラスで定義された構成クラス。 |
// ApplicationContext will be loaded from BaseConfig
@SpringJUnitConfig(BaseConfig::class) (1)
open class BaseTest {
// class body...
}
// ApplicationContext will be loaded from BaseConfig and ExtendedConfig
@SpringJUnitConfig(ExtendedConfig::class) (2)
class ExtendedTest : BaseTest() {
// class body...
}
1 | スーパークラスで定義された構成クラス。 |
2 | サブクラスで定義された構成クラス。 |
コンテキスト初期化子を使用する次の例では、ExtendedTest
の ApplicationContext
は BaseInitializer
および ExtendedInitializer
を使用して初期化されます。ただし、イニシャライザが呼び出される順序は、Spring の Ordered
インターフェースを実装するか、Spring の @Order
アノテーションまたは標準 @Priority
アノテーションが付けられているかによって異なります。次の例は、あるクラスが別のクラスを継承し、独自の初期化子とスーパークラスの初期化子の両方を使用する方法を示しています。
// ApplicationContext will be initialized by BaseInitializer
@SpringJUnitConfig(initializers = BaseInitializer.class) (1)
class BaseTest {
// class body...
}
// ApplicationContext will be initialized by BaseInitializer
// and ExtendedInitializer
@SpringJUnitConfig(initializers = ExtendedInitializer.class) (2)
class ExtendedTest extends BaseTest {
// class body...
}
1 | スーパークラスで定義された初期化子。 |
2 | サブクラスで定義された初期化子。 |
// ApplicationContext will be initialized by BaseInitializer
@SpringJUnitConfig(initializers = [BaseInitializer::class]) (1)
open class BaseTest {
// class body...
}
// ApplicationContext will be initialized by BaseInitializer
// and ExtendedInitializer
@SpringJUnitConfig(initializers = [ExtendedInitializer::class]) (2)
class ExtendedTest : BaseTest() {
// class body...
}
1 | スーパークラスで定義された初期化子。 |
2 | サブクラスで定義された初期化子。 |
環境プロファイルを使用したコンテキスト設定
Spring Framework には、環境とプロファイル(別名「Bean 定義プロファイル」)の概念に対するファーストクラスのサポートがあり、さまざまなテストシナリオで特定の Bean 定義プロファイルをアクティブにするように統合テストを構成できます。これは、テストクラスに @ActiveProfiles
アノテーションを付けて、テスト用に ApplicationContext
をロードするときにアクティブ化する必要があるプロファイルのリストを提供することで実現されます。
@ActiveProfiles は SmartContextLoader SPI のどの実装でも使用できますが、@ActiveProfiles は古い ContextLoader SPI の実装ではサポートされていません。 |
XML 構成と @Configuration
クラスを使用した 2 つの例を検討してください。
<!-- app-config.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<bean id="transferService"
class="com.bank.service.internal.DefaultTransferService">
<constructor-arg ref="accountRepository"/>
<constructor-arg ref="feePolicy"/>
</bean>
<bean id="accountRepository"
class="com.bank.repository.internal.JdbcAccountRepository">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="feePolicy"
class="com.bank.service.internal.ZeroFeePolicy"/>
<beans profile="dev">
<jdbc:embedded-database id="dataSource">
<jdbc:script
location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script
location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
<beans profile="default">
<jdbc:embedded-database id="dataSource">
<jdbc:script
location="classpath:com/bank/config/sql/schema.sql"/>
</jdbc:embedded-database>
</beans>
</beans>
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// test the transferService
}
}
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from "classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
lateinit var transferService: TransferService
@Test
fun testTransferService() {
// test the transferService
}
}
TransferServiceTest
が実行されると、その ApplicationContext
はクラスパスのルートにある app-config.xml
構成ファイルからロードされます。app-config.xml
を調べると、accountRepository
Bean が dataSource
Bean に依存していることがわかります。ただし、dataSource
はトップレベル Bean として定義されていません。代わりに、dataSource
は、production
プロファイル、dev
プロファイル、および default
プロファイルで 3 回定義されます。
TransferServiceTest
に @ActiveProfiles("dev")
のアノテーションを付けることにより、Spring TestContext フレームワークに、{"dev"}
に設定されたアクティブなプロファイルで ApplicationContext
をロードするよう指示します。その結果、組み込みデータベースが作成され、テストデータが入力され、accountRepository
Bean が開発 DataSource
への参照で接続されます。これはおそらく統合テストで必要なものです。
Bean を default
プロファイルに割り当てると便利な場合があります。デフォルトプロファイル内の Bean は、他のプロファイルが特にアクティブ化されていない場合にのみ含まれます。これを使用して、アプリケーションのデフォルト状態で使用される「フォールバック」Bean を定義できます。例: dev
および production
プロファイルのデータソースを明示的に提供できますが、どちらもアクティブでない場合は、メモリ内データソースをデフォルトとして定義できます。
次のコードリストは、XML の代わりに @Configuration
クラスを使用して同じ構成および統合テストを実装する方法を示しています。
@Configuration
@Profile("dev")
public class StandaloneDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("dev")
class StandaloneDataConfig {
@Bean
fun dataSource(): DataSource {
return EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build()
}
}
@Configuration
@Profile("production")
public class JndiDataConfig {
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
@Configuration
@Profile("production")
class JndiDataConfig {
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
val ctx = InitialContext()
return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
}
}
@Configuration
@Profile("default")
public class DefaultDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}
@Configuration
@Profile("default")
class DefaultDataConfig {
@Bean
fun dataSource(): DataSource {
return EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build()
}
}
@Configuration
public class TransferServiceConfig {
@Autowired DataSource dataSource;
@Bean
public TransferService transferService() {
return new DefaultTransferService(accountRepository(), feePolicy());
}
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public FeePolicy feePolicy() {
return new ZeroFeePolicy();
}
}
@Configuration
class TransferServiceConfig {
@Autowired
lateinit var dataSource: DataSource
@Bean
fun transferService(): TransferService {
return DefaultTransferService(accountRepository(), feePolicy())
}
@Bean
fun accountRepository(): AccountRepository {
return JdbcAccountRepository(dataSource)
}
@Bean
fun feePolicy(): FeePolicy {
return ZeroFeePolicy()
}
}
@SpringJUnitConfig({
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// test the transferService
}
}
@SpringJUnitConfig(
TransferServiceConfig::class,
StandaloneDataConfig::class,
JndiDataConfig::class,
DefaultDataConfig::class)
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
lateinit var transferService: TransferService
@Test
fun testTransferService() {
// test the transferService
}
}
このバリエーションでは、XML 構成を 4 つの独立した @Configuration
クラスに分割しました。
TransferServiceConfig
:@Autowired
を使用して、依存性注入によりdataSource
を取得します。StandaloneDataConfig
: 開発者テストに適した組み込みデータベースのdataSource
を定義します。JndiDataConfig
: 本番環境で JNDI から取得されるdataSource
を定義します。DefaultDataConfig
: アクティブなプロファイルがない場合に、デフォルトの組み込みデータベースのdataSource
を定義します。
XML ベースの構成例と同様に、TransferServiceTest
に @ActiveProfiles("dev")
のアノテーションを付けますが、今回は @ContextConfiguration
アノテーションを使用して 4 つの構成クラスすべてを指定します。テストクラスの本体自体は完全に変更されていません。
多くの場合、特定のプロジェクト内の複数のテストクラスで単一のプロファイルセットが使用されます。@ActiveProfiles
アノテーションの重複した宣言を避けるために、ベースクラスで @ActiveProfiles
を 1 回宣言することができ、サブクラスはベースクラスから @ActiveProfiles
構成を自動的に継承します。次の例では、@ActiveProfiles
の宣言(およびその他のアノテーション)が抽象スーパークラス AbstractIntegrationTest
に移動されています。
Spring Framework 5.3, 以降、テスト構成は、囲んでいるクラスから継承することもできます。詳細については、@Nested テストクラスの構成を参照してください。 |
@SpringJUnitConfig({
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
@SpringJUnitConfig(
TransferServiceConfig::class,
StandaloneDataConfig::class,
JndiDataConfig::class,
DefaultDataConfig::class)
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
// "dev" profile inherited from superclass
class TransferServiceTest extends AbstractIntegrationTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// test the transferService
}
}
// "dev" profile inherited from superclass
class TransferServiceTest : AbstractIntegrationTest() {
@Autowired
lateinit var transferService: TransferService
@Test
fun testTransferService() {
// test the transferService
}
}
@ActiveProfiles
は、次の例に示すように、アクティブプロファイルの継承を無効にするために使用できる inheritProfiles
属性もサポートしています。
// "dev" profile overridden with "production"
@ActiveProfiles(profiles = "production", inheritProfiles = false)
class ProductionTransferServiceTest extends AbstractIntegrationTest {
// test body
}
// "dev" profile overridden with "production"
@ActiveProfiles("production", inheritProfiles = false)
class ProductionTransferServiceTest : AbstractIntegrationTest() {
// test body
}
さらに、テストのアクティブなプロファイルを、宣言的にではなくプログラムによって解決することが必要になる場合があります。たとえば、次のものに基づいています。
現在のオペレーティングシステム。
テストが継続的インテグレーションビルドサーバーで実行されているかどうか。
特定の環境変数の存在。
カスタムクラスレベルのアノテーションの存在。
その他の懸念。
アクティブな Bean 定義プロファイルをプログラムで解決するために、カスタム ActiveProfilesResolver
を実装し、@ActiveProfiles
の resolver
属性を使用してそれを登録できます。詳細については、対応する javadoc を参照してください。次の例は、カスタム OperatingSystemActiveProfilesResolver
を実装および登録する方法を示しています。
// "dev" profile overridden programmatically via a custom resolver
@ActiveProfiles(
resolver = OperatingSystemActiveProfilesResolver.class,
inheritProfiles = false)
class TransferServiceTest extends AbstractIntegrationTest {
// test body
}
// "dev" profile overridden programmatically via a custom resolver
@ActiveProfiles(
resolver = OperatingSystemActiveProfilesResolver::class,
inheritProfiles = false)
class TransferServiceTest : AbstractIntegrationTest() {
// test body
}
public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver {
@Override
public String[] resolve(Class<?> testClass) {
String profile = ...;
// determine the value of profile based on the operating system
return new String[] {profile};
}
}
class OperatingSystemActiveProfilesResolver : ActiveProfilesResolver {
override fun resolve(testClass: Class<*>): Array<String> {
val profile: String = ...
// determine the value of profile based on the operating system
return arrayOf(profile)
}
}
テストプロパティソースを使用したコンテキスト構成
Spring Framework は、プロパティソースの階層を持つ環境の概念に対するファーストクラスのサポートを備えており、テスト固有のプロパティソースを使用して統合テストを構成できます。@Configuration
クラスで使用される @PropertySource
アノテーションとは対照的に、テストクラスで @TestPropertySource
アノテーションを宣言して、テストプロパティファイルまたはインラインプロパティのリソースの場所を宣言できます。これらのテストプロパティソースは、アノテーション付き統合テスト用に読み込まれた ApplicationContext
の Environment
の PropertySources
のセットに追加されます。
|
テストプロパティソースの宣言
@TestPropertySource
の locations
または value
属性を使用して、テストプロパティファイルを構成できます。
"classpath:/com/example/test.properties"
や "file:///path/to/file.xml"
など、従来のプロパティファイル形式と XML ベースのプロパティファイル形式の両方がサポートされています。
各パスは Spring Resource
として解釈されます。プレーンパス("test.properties"
など)は、テストクラスが定義されているパッケージに関連するクラスパスリソースとして扱われます。スラッシュで始まるパスは、絶対クラスパスリソースとして扱われます(例: "/org/example/test.xml"
)。URL を参照するパス(たとえば、接頭辞 classpath:
、file:
または http:
のパス)は、指定されたリソースプロトコルを使用してロードされます。リソースの場所のワイルドカード(*/.properties
など)は許可されません。各場所は、正確に 1 つの .properties
または .xml
リソースに評価される必要があります。
次の例では、テストプロパティファイルを使用しています。
@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
次の例では、2 つのインラインプロパティを設定します。
@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port: 4242"}) (1)
class MyIntegrationTests {
// class body...
}
1 | キーと値の構文の 2 つのバリエーションを使用して 2 つのプロパティを設定します。 |
@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port: 4242"]) (1)
class MyIntegrationTests {
// class body...
}
1 | キーと値の構文の 2 つのバリエーションを使用して 2 つのプロパティを設定します。 |
Spring Framework 5.2, 以降、 さらに、それぞれ 直接存在する |
デフォルトのプロパティファイルの検出
@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
属性を使用して宣言されたインラインプロパティによってオーバーライドされます。次の例は、ファイルとインラインの両方でプロパティを指定する方法を示しています。
@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
ファイルを使用して、サブクラスとそのスーパークラスの両方でプロパティを定義する方法を示しています。
@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
プロパティを使用してロードされます。次の例は、インラインプロパティを使用して、サブクラスとそのスーパークラスの両方でプロパティを定義する方法を示しています。
@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() {
// ...
}
動的プロパティソースを使用したコンテキスト構成
Spring Framework 5.2.5, 以降、TestContext フレームワークは、@DynamicPropertySource
アノテーションを介して動的プロパティをサポートします。このアノテーションは、統合テスト用にロードされた ApplicationContext
の Environment
内の PropertySources
のセットに動的な値を持つプロパティを追加する必要がある統合テストで使用できます。
|
クラスレベルで適用される @TestPropertySource
アノテーションとは対照的に、@DynamicPropertySource
は、Environment
に名前と値のペアを追加するために使用される単一の DynamicPropertyRegistry
引数を受け入れる static
メソッドに適用される必要があります。値は動的であり、プロパティが解決されたときにのみ呼び出される Supplier
を介して提供されます。通常、メソッド参照は値を提供するために使用されます。次の例では、Testcontainers プロジェクトを使用して Spring ApplicationContext
外の Redis コンテナーを管理しています。管理された Redis コンテナーの IP アドレスとポートは、redis.host
および redis.port
プロパティを介して、テストの ApplicationContext
内のコンポーネントで利用できるようになります。これらのプロパティは、Spring の Environment
抽象化を介してアクセスするか、たとえば、それぞれ @Value("${redis.host}")
および @Value("${redis.port}")
を介して、Spring 管理のコンポーネントに直接注入できます。
基本クラスで |
@SpringJUnitConfig(/* ... */)
@Testcontainers
class ExampleIntegrationTests {
@Container
static RedisContainer redis = new RedisContainer();
@DynamicPropertySource
static void redisProperties(DynamicPropertyRegistry registry) {
registry.add("redis.host", redis::getContainerIpAddress);
registry.add("redis.port", redis::getMappedPort);
}
// tests ...
}
@SpringJUnitConfig(/* ... */)
@Testcontainers
class ExampleIntegrationTests {
companion object {
@Container
@JvmStatic
val redis: RedisContainer = RedisContainer()
@DynamicPropertySource
@JvmStatic
fun redisProperties(registry: DynamicPropertyRegistry) {
registry.add("redis.host", redis::getContainerIpAddress)
registry.add("redis.port", redis::getMappedPort)
}
}
// tests ...
}
WebApplicationContext
のロード
TestContext フレームワークに標準の ApplicationContext
ではなく WebApplicationContext
をロードするよう指示するには、それぞれのテストクラスに @WebAppConfiguration
でアノテーションを付けます。
テストクラスに @WebAppConfiguration
が存在すると、TestContext フレームワーク(TCF)に対して、統合テストのために WebApplicationContext
(WAC)をロードする必要があります。バックグラウンドで、TCF は MockServletContext
が作成され、テストの WAC に提供されるようにします。デフォルトでは、MockServletContext
のベースリソースパスは src/main/webapp
に設定されています。これは、JVM のルートに対する相対パス(通常はプロジェクトへのパス)として解釈されます。Maven プロジェクトの Web アプリケーションのディレクトリ構造に精通している場合は、src/main/webapp
が WAR のルートのデフォルトの場所であることを知っています。このデフォルトをオーバーライドする必要がある場合は、@WebAppConfiguration
アノテーションへの代替パス(たとえば、@WebAppConfiguration("src/test/webapp")
)を提供できます。ファイルシステムではなくクラスパスからベースリソースパスを参照する場合は、Spring の classpath:
プレフィックスを使用できます。
WebApplicationContext
実装に対する Spring のテストサポートは、標準 ApplicationContext
実装に対するサポートと同等です。WebApplicationContext
でテストする場合、@ContextConfiguration
を使用して、XML 構成ファイル、Groovy スクリプト、または @Configuration
クラスを自由に宣言できます。@ActiveProfiles
、@TestExecutionListeners
、@Sql
、@Rollback
など、他のテストアノテーションも自由に使用できます。
このセクションの残りの例は、WebApplicationContext
をロードするためのさまざまな構成オプションの一部を示しています。次の例は、TestContext フレームワークの設定よりも規約に対するサポートを示しています。
@ExtendWith(SpringExtension.class)
// defaults to "file:src/main/webapp"
@WebAppConfiguration
// detects "WacTests-context.xml" in the same package
// or static nested @Configuration classes
@ContextConfiguration
class WacTests {
//...
}
@ExtendWith(SpringExtension::class)
// defaults to "file:src/main/webapp"
@WebAppConfiguration
// detects "WacTests-context.xml" in the same package
// or static nested @Configuration classes
@ContextConfiguration
class WacTests {
//...
}
リソースベースパスを指定せずに @WebAppConfiguration
でテストクラスにアノテーションを付けると、リソースパスは事実上 file:src/main/webapp
にデフォルト設定されます。同様に、リソース locations
、コンポーネント classes
、またはコンテキスト initializers
を指定せずに @ContextConfiguration
を宣言すると、Spring は規則(つまり、WacTests
クラスまたは静的ネスト @Configuration
クラスと同じパッケージ内の WacTests-context.xml
)を使用して構成の存在を検出しようとします。
次の例は、@WebAppConfiguration
でリソースベースパスを明示的に宣言し、@ContextConfiguration
で XML リソースの場所を明示的に宣言する方法を示しています。
@ExtendWith(SpringExtension.class)
// file system resource
@WebAppConfiguration("webapp")
// classpath resource
@ContextConfiguration("/spring/test-servlet-config.xml")
class WacTests {
//...
}
@ExtendWith(SpringExtension::class)
// file system resource
@WebAppConfiguration("webapp")
// classpath resource
@ContextConfiguration("/spring/test-servlet-config.xml")
class WacTests {
//...
}
ここで注意すべき重要なことは、これらの 2 つのアノテーションを持つパスのセマンティクスが異なることです。デフォルトでは、@WebAppConfiguration
リソースパスはファイルシステムベースですが、@ContextConfiguration
リソースロケーションはクラスパスベースです。
次の例は、Spring リソースプレフィックスを指定することで、両方のアノテーションのデフォルトのリソースセマンティクスをオーバーライドできることを示しています。
@ExtendWith(SpringExtension.class)
// classpath resource
@WebAppConfiguration("classpath:test-web-resources")
// file system resource
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")
class WacTests {
//...
}
@ExtendWith(SpringExtension::class)
// classpath resource
@WebAppConfiguration("classpath:test-web-resources")
// file system resource
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")
class WacTests {
//...
}
この例のコメントを前の例と比較してください。
包括的な Web テストサポートを提供するために、TestContext フレームワークには ServletTestExecutionListener
があり、デフォルトで有効になっています。WebApplicationContext
に対してテストする場合、この TestExecutionListener
は、各テストメソッドの前に Spring Web の RequestContextHolder
を使用してデフォルトのスレッドローカル状態を設定し、@WebAppConfiguration
で構成されたベースリソースパスに基づいて MockHttpServletRequest
、MockHttpServletResponse
、ServletWebRequest
を作成します。ServletTestExecutionListener
は、MockHttpServletResponse
および ServletWebRequest
がテストインスタンスに挿入できることも保証し、テストが完了すると、スレッドローカル状態をクリーンアップします。
テスト用に WebApplicationContext
をロードしたら、たとえば、テストフィクスチャをセットアップしたり、Web コンポーネントを呼び出した後にアサーションを実行したりするために、Web モックと対話する必要がある場合があります。次の例は、どのモックをテストインスタンスに自動接続できるかを示しています。WebApplicationContext
と MockServletContext
は両方ともテストスイート全体でキャッシュされるのに対し、他のモックは ServletTestExecutionListener
によってテストメソッドごとに管理されることに注意してください。
@SpringJUnitWebConfig
class WacTests {
@Autowired
WebApplicationContext wac; // cached
@Autowired
MockServletContext servletContext; // cached
@Autowired
MockHttpSession session;
@Autowired
MockHttpServletRequest request;
@Autowired
MockHttpServletResponse response;
@Autowired
ServletWebRequest webRequest;
//...
}
@SpringJUnitWebConfig
class WacTests {
@Autowired
lateinit var wac: WebApplicationContext // cached
@Autowired
lateinit var servletContext: MockServletContext // cached
@Autowired
lateinit var session: MockHttpSession
@Autowired
lateinit var request: MockHttpServletRequest
@Autowired
lateinit var response: MockHttpServletResponse
@Autowired
lateinit var webRequest: ServletWebRequest
//...
}
コンテキストキャッシング
TestContext フレームワークがテスト用に ApplicationContext
(または WebApplicationContext
)をロードすると、そのコンテキストはキャッシュされ、同じテストスイート内で同じ一意のコンテキスト構成を宣言する後続のすべてのテストで再利用されます。キャッシングの仕組みを理解するには、「一意」と「テストスイート」の意味を理解することが重要です。
ApplicationContext
は、ロードに使用される構成パラメーターの組み合わせによって一意に識別できます。構成パラメーターの一意の組み合わせを使用して、コンテキストがキャッシュされるキーを生成します。TestContext フレームワークは、次の構成パラメーターを使用してコンテキストキャッシュキーを構築します。
locations
(@ContextConfiguration
から)classes
(@ContextConfiguration
から)contextInitializerClasses
(@ContextConfiguration
から)contextCustomizers
(ContextCustomizerFactory
から)–これには、@DynamicPropertySource
メソッド、@MockBean
や@SpyBean
などの Spring Boot のテストサポートからのさまざまな機能が含まれます。contextLoader
(@ContextConfiguration
から)parent
(@ContextHierarchy
から)activeProfiles
(@ActiveProfiles
から)propertySourceLocations
(@TestPropertySource
から)propertySourceProperties
(@TestPropertySource
から)resourceBasePath
(@WebAppConfiguration
から)
例: TestClassA
が @ContextConfiguration
の locations
(または value
)属性に {"app-config.xml", "test-config.xml"}
を指定する場合、TestContext フレームワークは対応する ApplicationContext
をロードし、それらのロケーションのみに基づくキーの static
コンテキストキャッシュに格納します。そのため、TestClassB
がその場所の {"app-config.xml", "test-config.xml"}
も(継承を介して明示的または暗黙的に)定義し、@WebAppConfiguration
、異なる ContextLoader
、異なるアクティブプロファイル、異なるコンテキスト初期化子、異なるテストプロパティソース、または異なる親コンテキストを定義しない場合、同じ ApplicationContext
は両方のテストクラスで共有されます。これは、アプリケーションコンテキストを読み込むためのセットアップコストが 1 回(テストスイートごとに)発生するだけであり、その後のテスト実行がはるかに高速であることを意味します。
テストスイートと分岐プロセス Spring TestContext フレームワークは、アプリケーションコンテキストを静的キャッシュに格納します。これは、コンテキストが文字通り キャッシングメカニズムを活用するには、すべてのテストを同じプロセスまたはテストスイート内で実行する必要があります。これは、IDE 内のグループとしてすべてのテストを実行することで実現できます。同様に、Ant、Maven、Gradle などのビルドフレームワークでテストを実行する場合、ビルドフレームワークがテスト間で分岐しないことを確認することが重要です。例:Maven Surefire プラグインの |
コンテキストキャッシュのサイズは、デフォルトの最大サイズである 32 に制限されています。最大サイズに達すると、使用頻度が最も低い(LRU)エビクションポリシーが使用され、古いコンテキストが排除されます。spring.test.context.cache.maxSize
という名前の JVM システムプロパティを設定することにより、コマンドラインまたはビルドスクリプトから最大サイズを構成できます。別の方法として、SpringProperties
API を使用してプログラムで同じプロパティを設定できます。
特定のテストスイート内に多数のアプリケーションコンテキストをロードすると、スイートの実行に不必要に長い時間がかかる可能性があるため、ロードおよびキャッシュされたコンテキストの数を正確に把握することはしばしば有益です。基盤となるコンテキストキャッシュの統計を表示するには、org.springframework.test.context.cache
ロギングカテゴリのログレベルを DEBUG
に設定できます。
万が一、テストによってアプリケーションコンテキストが破損し、リロードが必要になった場合(たとえば、Bean 定義またはアプリケーションオブジェクトの状態を変更することにより)、テストクラスまたはテストメソッドに @DirtiesContext
アノテーションを付けることができます(の @DirtiesContext
の説明を参照)。Spring Test アノテーション)。これは、同じアプリケーションコンテキストを必要とする次のテストを実行する前に、キャッシュからコンテキストを削除してアプリケーションコンテキストを再構築するように Spring に指示します。@DirtiesContext
アノテーションのサポートは、デフォルトで有効になっている DirtiesContextBeforeModesTestExecutionListener
および DirtiesContextTestExecutionListener
によって提供されることに注意してください。
ApplicationContext のライフサイクルとコンソールログ Spring TestContext フレームワークで実行されたテストをデバッグする必要がある場合は、コンソール出力(つまり、 Spring Framework 自体または テストの テスト用の
特定のテストメソッドの後に Spring |
コンテキストの階層
ロードされた Spring ApplicationContext
に依存する統合テストを作成する場合、多くの場合、単一のコンテキストに対してテストするだけで十分です。ただし、ApplicationContext
インスタンスの階層に対してテストすることが有益であるか、必要でさえある場合があります。例:Spring MVC Web アプリケーションを開発している場合、通常、Spring の ContextLoaderListener
によってロードされたルート WebApplicationContext
と、Spring の DispatcherServlet
によってロードされた子 WebApplicationContext
があります。これにより、共有コンポーネントとインフラストラクチャ設定がルートコンテキストで宣言され、Web 固有のコンポーネントによって子コンテキストで消費される親子コンテキスト階層が作成されます。別のユースケースは Spring Batch アプリケーションにあります。Spring Batch アプリケーションでは、多くの場合、共有バッチインフラストラクチャの構成を提供する親コンテキストと、特定のバッチジョブの構成用の子コンテキストがあります。
個々のテストクラスまたはテストクラス階層内で、@ContextHierarchy
アノテーションを使用してコンテキスト構成を宣言することにより、コンテキスト階層を使用する統合テストを作成できます。テストクラス階層内の複数のクラスでコンテキスト階層が宣言されている場合、コンテキスト階層の特定の名前付きレベルのコンテキスト構成をマージまたはオーバーライドすることもできます。階層内の特定のレベルの構成をマージする場合、構成リソースタイプ(つまり、XML 構成ファイルまたはコンポーネントクラス)は一貫している必要があります。それ以外の場合、異なるリソースタイプを使用して設定されたコンテキスト階層の異なるレベルを持つことは完全に受け入れられます。
このセクションの残りの JUnit Jupiter ベースの例は、コンテキスト階層の使用を必要とする統合テストの一般的な構成シナリオを示しています。
ControllerIntegrationTests
は、ルート WebApplicationContext
(TestAppConfig
@Configuration
クラスを使用してロードされる)とディスパッチャーサーブレット WebApplicationContext
(WebConfig
@Configuration
を使用してロードされる)の 2 つのレベルで構成されるコンテキスト階層を宣言することにより、Spring MVC Web アプリケーションの典型的な統合テストシナリオを表します。クラス)。テストインスタンスに自動接続される WebApplicationContext
は、子コンテキスト(つまり、階層内の最下位コンテキスト)のものです。次のリストは、この構成シナリオを示しています。
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = TestAppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
class ControllerIntegrationTests {
@Autowired
WebApplicationContext wac;
// ...
}
@ExtendWith(SpringExtension::class)
@WebAppConfiguration
@ContextHierarchy(
ContextConfiguration(classes = [TestAppConfig::class]),
ContextConfiguration(classes = [WebConfig::class]))
class ControllerIntegrationTests {
@Autowired
lateinit var wac: WebApplicationContext
// ...
}
この例のテストクラスは、テストクラス階層内のコンテキスト階層を定義します。AbstractWebTests
は、Spring を使用した Web アプリケーションでルート WebApplicationContext
の構成を宣言します。ただし、AbstractWebTests
は @ContextHierarchy
を宣言しないことに注意してください。AbstractWebTests
のサブクラスは、オプションでコンテキスト階層に参加したり、@ContextConfiguration
の標準的なセマンティクスに従うことができます。SoapWebServiceTests
と RestWebServiceTests
は両方とも AbstractWebTests
を継承し、@ContextHierarchy
を使用してコンテキスト階層を定義します。その結果、3 つのアプリケーションコンテキスト(@ContextConfiguration
の宣言ごとに 1 つ)が読み込まれ、AbstractWebTests
の構成に基づいて読み込まれたアプリケーションコンテキストが、具象サブクラスに読み込まれた各コンテキストの親コンテキストとして設定されます。次のリストは、この構成シナリオを示しています。
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
public abstract class AbstractWebTests {}
@ContextHierarchy(@ContextConfiguration("/spring/soap-ws-config.xml"))
public class SoapWebServiceTests extends AbstractWebTests {}
@ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml"))
public class RestWebServiceTests extends AbstractWebTests {}
@ExtendWith(SpringExtension::class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
abstract class AbstractWebTests
@ContextHierarchy(ContextConfiguration("/spring/soap-ws-config.xml"))
class SoapWebServiceTests : AbstractWebTests()
@ContextHierarchy(ContextConfiguration("/spring/rest-ws-config.xml"))
class RestWebServiceTests : AbstractWebTests()
この例のクラスは、コンテキスト階層内の特定のレベルの構成をマージするための名前付き階層レベルの使用を示しています。BaseTests
は、階層内の 2 つのレベル、parent
および child
を定義します。ExtendedTests
は BaseTests
を継承し、@ContextConfiguration
の name
属性で宣言された名前が両方とも child
であることを確認することにより、child
階層レベルのコンテキスト構成をマージするように Spring TestContext フレームワークに指示します。その結果、/app-config.xml
用、/user-config.xml
用、{"/user-config.xml", "/order-config.xml"}
用の 3 つのアプリケーションコンテキストがロードされます。前の例と同様に、/app-config.xml
からロードされたアプリケーションコンテキストは、/user-config.xml
および {"/user-config.xml", "/order-config.xml"}
からロードされたコンテキストの親コンテキストとして設定されます。次のリストは、この構成シナリオを示しています。
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
@ContextConfiguration(name = "child", locations = "/user-config.xml")
})
class BaseTests {}
@ContextHierarchy(
@ContextConfiguration(name = "child", locations = "/order-config.xml")
)
class ExtendedTests extends BaseTests {}
@ExtendWith(SpringExtension::class)
@ContextHierarchy(
ContextConfiguration(name = "parent", locations = ["/app-config.xml"]),
ContextConfiguration(name = "child", locations = ["/user-config.xml"]))
open class BaseTests {}
@ContextHierarchy(
ContextConfiguration(name = "child", locations = ["/order-config.xml"])
)
class ExtendedTests : BaseTests() {}
前の例とは対照的に、この例は、@ContextConfiguration
の inheritLocations
フラグを false
に設定することにより、コンテキスト階層内の特定の名前付きレベルの構成をオーバーライドする方法を示しています。その結果、ExtendedTests
のアプリケーションコンテキストは /test-user-config.xml
からのみロードされ、/app-config.xml
からロードされたコンテキストに親が設定されます。次のリストは、この構成シナリオを示しています。
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
@ContextConfiguration(name = "child", locations = "/user-config.xml")
})
class BaseTests {}
@ContextHierarchy(
@ContextConfiguration(
name = "child",
locations = "/test-user-config.xml",
inheritLocations = false
))
class ExtendedTests extends BaseTests {}
@ExtendWith(SpringExtension::class)
@ContextHierarchy(
ContextConfiguration(name = "parent", locations = ["/app-config.xml"]),
ContextConfiguration(name = "child", locations = ["/user-config.xml"]))
open class BaseTests {}
@ContextHierarchy(
ContextConfiguration(
name = "child",
locations = ["/test-user-config.xml"],
inheritLocations = false
))
class ExtendedTests : BaseTests() {}
コンテキスト階層内のコンテキストを汚す コンテキストがコンテキスト階層の一部として構成されているテストで @DirtiesContext を使用する場合、hierarchyMode フラグを使用してコンテキストキャッシュをクリアする方法を制御できます。詳細については、Spring Test アノテーションの @DirtiesContext および @DirtiesContext (Javadoc) javadoc の説明を参照してください。 |
3.5.7. テストフィクスチャの依存性注入
DependencyInjectionTestExecutionListener
(デフォルトで設定)を使用すると、テストインスタンスの依存関係は、@ContextConfiguration
または関連するアノテーションで設定したアプリケーションコンテキストの Bean から注入されます。選択するアノテーションと、setter メソッドまたはフィールドに配置するかどうかに応じて、setter インジェクション、フィールドインジェクション、またはその両方を使用できます。JUnit Jupiter を使用している場合は、オプションでコンストラクター注入を使用することもできます(SpringExtension
による依存性注入を参照)。Spring のアノテーションベースの注入サポートとの一貫性を保つため、Spring の @Autowired
アノテーションまたは JSR-330 の @Inject
アノテーションをフィールドおよび setter 注入に使用することもできます。
JUnit Jupiter 以外のテストフレームワークの場合、TestContext フレームワークはテストクラスのインスタンス化に参加しません。コンストラクターに @Autowired または @Inject を使用しても、テストクラスには効果がありません。 |
フィールドインジェクションは製品コードでは推奨されていませんが、実際にはテストコードではフィールドインジェクションは非常に自然です。違いの理由は、テストクラスを直接インスタンス化しないことです。テストクラスで public コンストラクターまたは setter メソッドを呼び出す必要はありません。 |
@Autowired
は type によるオートワイヤーの実行に使用されるため、同じタイプの Bean 定義が複数ある場合、これらの特定の Bean にこのアプローチを当てにすることはできません。その場合、@Autowired
を @Qualifier
と組み合わせて使用できます。@Inject
を @Named
と組み合わせて使用することもできます。あるいは、テストクラスが ApplicationContext
にアクセスできる場合は、applicationContext.getBean("titleRepository", TitleRepository.class)
の呼び出しを使用して明示的なルックアップを実行できます。
テストインスタンスに依存性注入を適用したくない場合は、フィールドまたは setter メソッドに @Autowired
または @Inject
でアノテーションを付けないでください。あるいは、@TestExecutionListeners
を使用してクラスを明示的に構成し、リスナーのリストから DependencyInjectionTestExecutionListener.class
を省略することにより、依存性注入を完全に無効にすることができます。
ゴールセクションで概説されているように、HibernateTitleRepository
クラスをテストするシナリオを検討してください。次の 2 つのコードリストは、フィールドでの @Autowired
および setter メソッドの使用箇所を示しています。アプリケーションコンテキスト構成は、すべてのサンプルコードリストの後に表示されます。
次のコードリストの依存性注入動作は、JUnit Jupiter に固有のものではありません。同じ DI テクニックは、サポートされているテストフレームワークと組み合わせて使用できます。 次の例では、 |
最初のコードリストは、フィールドインジェクションに @Autowired
を使用するテストクラスの JUnit Jupiter ベースの実装を示しています。
@ExtendWith(SpringExtension.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {
// this instance will be dependency injected by type
@Autowired
HibernateTitleRepository titleRepository;
@Test
void findById() {
Title title = titleRepository.findById(new Long(10));
assertNotNull(title);
}
}
@ExtendWith(SpringExtension::class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {
// this instance will be dependency injected by type
@Autowired
lateinit var titleRepository: HibernateTitleRepository
@Test
fun findById() {
val title = titleRepository.findById(10)
assertNotNull(title)
}
}
または、次のように、setter インジェクションに @Autowired
を使用するようにクラスを構成できます。
@ExtendWith(SpringExtension.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {
// this instance will be dependency injected by type
HibernateTitleRepository titleRepository;
@Autowired
void setTitleRepository(HibernateTitleRepository titleRepository) {
this.titleRepository = titleRepository;
}
@Test
void findById() {
Title title = titleRepository.findById(new Long(10));
assertNotNull(title);
}
}
@ExtendWith(SpringExtension::class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {
// this instance will be dependency injected by type
lateinit var titleRepository: HibernateTitleRepository
@Autowired
fun setTitleRepository(titleRepository: HibernateTitleRepository) {
this.titleRepository = titleRepository
}
@Test
fun findById() {
val title = titleRepository.findById(10)
assertNotNull(title)
}
}
上記のコードリストでは、@ContextConfiguration
アノテーション(つまり repository-config.xml
)によって参照される同じ XML コンテキストファイルを使用しています。以下にこの構成を示します。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- this bean will be injected into the HibernateTitleRepositoryTests class -->
<bean id="titleRepository" class="com.foo.repository.hibernate.HibernateTitleRepository">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- configuration elided for brevity -->
</bean>
</beans>
setter メソッドの 1 つで Java
Kotlin
指定された修飾子の値は、注入する特定の |
3.5.8. リクエストおよびセッションスコープの Bean のテスト
Spring は初期からリクエストおよびセッションスコープの Bean をサポートしており、次の手順に従ってリクエストスコープおよびセッションスコープの Bean をテストできます。
テストクラスに
@WebAppConfiguration
のアノテーションを付けて、WebApplicationContext
がテスト用にロードされていることを確認します。モックリクエストまたはセッションをテストインスタンスに挿入し、必要に応じてテストフィクスチャを準備します。
構成済みの
WebApplicationContext
から取得した Web コンポーネントを呼び出します(依存性注入を使用)。モックに対してアサーションを実行します。
次のコードスニペットは、ログインユースケースの XML 構成を示しています。userService
Bean は、リクエストスコープの loginAction
Bean に依存していることに注意してください。また、LoginAction
は、現在の HTTP リクエストからユーザー名とパスワードを取得する SpEL 式を使用してインスタンス化されます。このテストでは、TestContext フレームワークによって管理されるモックを介してこれらのリクエストパラメーターを構成します。次のリストは、このユースケースの構成を示しています。
<beans>
<bean id="userService" class="com.example.SimpleUserService"
c:loginAction-ref="loginAction"/>
<bean id="loginAction" class="com.example.LoginAction"
c:username="#{request.getParameter('user')}"
c:password="#{request.getParameter('pswd')}"
scope="request">
<aop:scoped-proxy/>
</bean>
</beans>
RequestScopedBeanTests
では、UserService
(つまりテスト対象)と MockHttpServletRequest
の両方をテストインスタンスに注入します。requestScope()
テストメソッド内で、提供された MockHttpServletRequest
でリクエストパラメーターを設定することにより、テストフィクスチャをセットアップします。userService
で loginUser()
メソッドが呼び出されると、ユーザーサービスが現在の MockHttpServletRequest
(つまり、パラメーターを設定したもの)のリクエスト範囲 loginAction
にアクセスできることが保証されます。その後、ユーザー名とパスワードの既知の入力に基づいて、結果に対してアサーションを実行できます。次のリストは、その方法を示しています。
@SpringJUnitWebConfig
class RequestScopedBeanTests {
@Autowired UserService userService;
@Autowired MockHttpServletRequest request;
@Test
void requestScope() {
request.setParameter("user", "enigma");
request.setParameter("pswd", "$pr!ng");
LoginResults results = userService.loginUser();
// assert results
}
}
@SpringJUnitWebConfig
class RequestScopedBeanTests {
@Autowired lateinit var userService: UserService
@Autowired lateinit var request: MockHttpServletRequest
@Test
fun requestScope() {
request.setParameter("user", "enigma")
request.setParameter("pswd", "\$pr!ng")
val results = userService.loginUser()
// assert results
}
}
次のコードスニペットは、リクエストスコープの Bean について先ほど見たものに似ています。ただし、今回は、userService
Bean はセッションスコープの userPreferences
Bean に依存しています。UserPreferences
Bean は、現在の HTTP セッションからテーマを取得する SpEL 式を使用してインスタンス化されることに注意してください。このテストでは、TestContext フレームワークによって管理されるモックセッションでテーマを構成する必要があります。次の例は、その方法を示しています。
<beans>
<bean id="userService" class="com.example.SimpleUserService"
c:userPreferences-ref="userPreferences" />
<bean id="userPreferences" class="com.example.UserPreferences"
c:theme="#{session.getAttribute('theme')}"
scope="session">
<aop:scoped-proxy/>
</bean>
</beans>
SessionScopedBeanTests
では、UserService
と MockHttpSession
をテストインスタンスに注入します。sessionScope()
テストメソッド内で、提供された MockHttpSession
で予想される theme
属性を設定することにより、テストフィクスチャをセットアップします。userService
で processUserPreferences()
メソッドが呼び出されると、ユーザーサービスが現在の MockHttpSession
のセッションスコープの userPreferences
にアクセスできることが保証され、構成されたテーマに基づいて結果に対してアサーションを実行できます。次の例は、その方法を示しています。
@SpringJUnitWebConfig
class SessionScopedBeanTests {
@Autowired UserService userService;
@Autowired MockHttpSession session;
@Test
void sessionScope() throws Exception {
session.setAttribute("theme", "blue");
Results results = userService.processUserPreferences();
// assert results
}
}
@SpringJUnitWebConfig
class SessionScopedBeanTests {
@Autowired lateinit var userService: UserService
@Autowired lateinit var session: MockHttpSession
@Test
fun sessionScope() {
session.setAttribute("theme", "blue")
val results = userService.processUserPreferences()
// assert results
}
}
3.5.9. トランザクション管理
TestContext フレームワークでは、テストクラスで @TestExecutionListeners
を明示的に宣言しなくても、トランザクションはデフォルトで設定される TransactionalTestExecutionListener
によって管理されます。ただし、トランザクションのサポートを有効にするには、@ContextConfiguration
セマンティクスでロードされる ApplicationContext
で PlatformTransactionManager
Bean を構成する必要があります(詳細については後で説明します)。さらに、テストのクラスレベルまたはメソッドレベルで Spring の @Transactional
アノテーションを宣言する必要があります。
テスト管理されたトランザクション
テスト管理トランザクションは、TransactionalTestExecutionListener
を使用して宣言的に管理されるトランザクション、または TestTransaction
(後述)を使用してプログラムによって管理されるトランザクションです。このようなトランザクションを、Spring 管理のトランザクション(テスト用に読み込まれた ApplicationContext
内の Spring によって直接管理されるもの)やアプリケーション管理のトランザクション(テストによって呼び出されるアプリケーションコード内でプログラムによって管理されるもの)と混同しないでください。通常、Spring 管理およびアプリケーション管理のトランザクションは、テスト管理のトランザクションに参加します。ただし、Spring 管理またはアプリケーション管理のトランザクションが REQUIRED
または SUPPORTS
以外の伝搬タイプで構成されている場合は注意が必要です(詳細については、トランザクションの伝搬に関する説明を参照してください)。
プリエンプティブタイムアウトとテスト管理されたトランザクション Spring のテスト管理トランザクションと組み合わせて、テストフレームワークからプリエンプティブタイムアウトを使用する場合は注意が必要です。 特に、Spring のテストサポートは、現在のテストメソッドが呼び出される前に、トランザクション状態を( これが発生する可能性のある状況には、以下が含まれますが、これらに限定されません。
|
トランザクションの有効化と無効化
テストメソッドに @Transactional
アノテーションを付けると、トランザクション内でテストが実行され、デフォルトでは、テストの補完後に自動的にロールバックされます。テストクラスに @Transactional
アノテーションが付けられている場合、そのクラス階層内の各テストメソッドはトランザクション内で実行されます。(クラスまたはメソッドレベルで) @Transactional
アノテーションが付けられていないテストメソッドは、トランザクション内で実行されません。@Transactional
は、テストライフサイクルメソッドではサポートされていないことに注意してください。たとえば、JUnit Jupiter の @BeforeAll
、@BeforeEach
でアノテーションが付けられたメソッドなどです。さらに、@Transactional
でアノテーションが付けられているが、propagation
属性が NOT_SUPPORTED
または NEVER
に設定されているテストはトランザクション内で実行されません。
属性 | テスト管理されたトランザクションでサポート |
---|---|
| はい |
|
|
| いいえ |
| いいえ |
| いいえ |
| いいえ: 代わりに |
| いいえ: 代わりに |
メソッドレベルのライフサイクルメソッド — たとえば、JUnit Jupiter の トランザクション内でスイートレベルまたはクラスレベルのライフサイクルメソッドでコードを実行する必要がある場合は、対応する |
AbstractTransactionalJUnit4SpringContextTests
および AbstractTransactionalTestNGSpringContextTests
は、クラスレベルでのトランザクションサポート用に事前構成されていることに注意してください。
次の例は、Hibernate ベースの UserRepository
の統合テストを記述する一般的なシナリオを示しています。
@SpringJUnitConfig(TestConfig.class)
@Transactional
class HibernateUserRepositoryTests {
@Autowired
HibernateUserRepository repository;
@Autowired
SessionFactory sessionFactory;
JdbcTemplate jdbcTemplate;
@Autowired
void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
void createUser() {
// track initial state in test database:
final int count = countRowsInTable("user");
User user = new User(...);
repository.save(user);
// Manual flush is required to avoid false positive in test
sessionFactory.getCurrentSession().flush();
assertNumUsers(count + 1);
}
private int countRowsInTable(String tableName) {
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
}
private void assertNumUsers(int expected) {
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));
}
}
@SpringJUnitConfig(TestConfig::class)
@Transactional
class HibernateUserRepositoryTests {
@Autowired
lateinit var repository: HibernateUserRepository
@Autowired
lateinit var sessionFactory: SessionFactory
lateinit var jdbcTemplate: JdbcTemplate
@Autowired
fun setDataSource(dataSource: DataSource) {
this.jdbcTemplate = JdbcTemplate(dataSource)
}
@Test
fun createUser() {
// track initial state in test database:
val count = countRowsInTable("user")
val user = User()
repository.save(user)
// Manual flush is required to avoid false positive in test
sessionFactory.getCurrentSession().flush()
assertNumUsers(count + 1)
}
private fun countRowsInTable(tableName: String): Int {
return JdbcTestUtils.countRowsInTable(jdbcTemplate, tableName)
}
private fun assertNumUsers(expected: Int) {
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"))
}
}
トランザクションのロールバックとコミットの動作で説明したように、データベースに加えられた変更は TransactionalTestExecutionListener
によって自動的にロールバックされるため、createUser()
メソッドの実行後にデータベースをクリーンアップする必要はありません。
トランザクションのロールバックとコミットの動作
デフォルトでは、テストトランザクションはテストの補完後に自動的にロールバックされます。ただし、@Commit
および @Rollback
アノテーションを使用して、トランザクションのコミットおよびロールバックの動作を宣言的に構成できます。詳細については、アノテーションサポートセクションの対応するエントリを参照してください。
プログラムによるトランザクション管理
TestTransaction
の静的メソッドを使用して、プログラムでテスト管理されたトランザクションと対話できます。例:テストメソッド内、メソッドの前、メソッドの後に TestTransaction
を使用して、現在のテスト管理トランザクションを開始または終了したり、ロールバックまたはコミットのために現在のテスト管理トランザクションを構成したりできます。TransactionalTestExecutionListener
が有効になると、TestTransaction
のサポートが自動的に使用可能になります。
次の例は、TestTransaction
の機能の一部を示しています。詳細については、TestTransaction
(Javadoc) の javadoc を参照してください。
@ContextConfiguration(classes = TestConfig.class)
public class ProgrammaticTransactionManagementTests extends
AbstractTransactionalJUnit4SpringContextTests {
@Test
public void transactionalTest() {
// assert initial state in test database:
assertNumUsers(2);
deleteFromTables("user");
// changes to the database will be committed!
TestTransaction.flagForCommit();
TestTransaction.end();
assertFalse(TestTransaction.isActive());
assertNumUsers(0);
TestTransaction.start();
// perform other actions against the database that will
// be automatically rolled back after the test completes...
}
protected void assertNumUsers(int expected) {
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"));
}
}
@ContextConfiguration(classes = [TestConfig::class])
class ProgrammaticTransactionManagementTests : AbstractTransactionalJUnit4SpringContextTests() {
@Test
fun transactionalTest() {
// assert initial state in test database:
assertNumUsers(2)
deleteFromTables("user")
// changes to the database will be committed!
TestTransaction.flagForCommit()
TestTransaction.end()
assertFalse(TestTransaction.isActive())
assertNumUsers(0)
TestTransaction.start()
// perform other actions against the database that will
// be automatically rolled back after the test completes...
}
protected fun assertNumUsers(expected: Int) {
assertEquals("Number of rows in the [user] table.", expected, countRowsInTable("user"))
}
}
トランザクション外でのコードの実行
場合によっては、トランザクションテストメソッドの前または後に、トランザクションコンテキストの外で特定のコードを実行する必要があります。たとえば、テストを実行する前にデータベースの初期状態を確認したり、テストの実行後に予想されるトランザクションコミット動作を確認したりします(テストはトランザクションをコミットするように構成されました)。TransactionalTestExecutionListener
は、まさにそのようなシナリオの @BeforeTransaction
および @AfterTransaction
アノテーションをサポートします。テストクラスの void
メソッドまたはテストインターフェースの void
デフォルトメソッドにこれらのアノテーションのいずれかでアノテーションを付けることができます。TransactionalTestExecutionListener
は、前トランザクションメソッドまたは後トランザクションメソッドが適切なタイミングで実行されるようにします。
before メソッド(JUnit Jupiter の @BeforeEach アノテーションが付けられたメソッドなど)および after メソッド(JUnit Jupiter の @AfterEach アノテーションが付けられたメソッドなど)は、トランザクション内で実行されます。さらに、@BeforeTransaction または @AfterTransaction アノテーションが付けられたメソッドは、トランザクション内で実行するように構成されていないテストメソッドでは実行されません。 |
トランザクションマネージャーの構成
TransactionalTestExecutionListener
は、テストのために PlatformTransactionManager
Bean が Spring ApplicationContext
で定義されることを期待しています。テストの ApplicationContext
内に PlatformTransactionManager
のインスタンスが複数ある場合は、@Transactional("myTxMgr")
または @Transactional(transactionManager = "myTxMgr")
を使用して修飾子を宣言するか、@Configuration
クラスによって TransactionManagementConfigurer
を実装できます。テストの ApplicationContext
でトランザクションマネージャーを検索するために使用されるアルゴリズムの詳細については、TestContextTransactionUtils.retrieveTransactionManager()
の javadoc を参照してください。
すべてのトランザクション関連のアノテーションのデモンストレーション
次の JUnit Jupiter ベースの例は、すべてのトランザクション関連のアノテーションを強調する架空の統合テストシナリオを示しています。この例は、ベストプラクティスを示すことを目的としたものではなく、これらのアノテーションの使用方法を示すことを目的としています。詳細および構成例については、アノテーションサポートのセクションを参照してください。@Sql
のトランザクション管理には、@Sql
を使用して、デフォルトのトランザクションロールバックセマンティクスで宣言的な SQL スクリプトを実行する追加の例が含まれています。次の例は、関連するアノテーションを示しています。
@SpringJUnitConfig
@Transactional(transactionManager = "txMgr")
@Commit
class FictitiousTransactionalTest {
@BeforeTransaction
void verifyInitialDatabaseState() {
// logic to verify the initial state before a transaction is started
}
@BeforeEach
void setUpTestDataWithinTransaction() {
// set up test data within the transaction
}
@Test
// overrides the class-level @Commit setting
@Rollback
void modifyDatabaseWithinTransaction() {
// logic which uses the test data and modifies database state
}
@AfterEach
void tearDownWithinTransaction() {
// run "tear down" logic within the transaction
}
@AfterTransaction
void verifyFinalDatabaseState() {
// logic to verify the final state after transaction has rolled back
}
}
@SpringJUnitConfig
@Transactional(transactionManager = "txMgr")
@Commit
class FictitiousTransactionalTest {
@BeforeTransaction
fun verifyInitialDatabaseState() {
// logic to verify the initial state before a transaction is started
}
@BeforeEach
fun setUpTestDataWithinTransaction() {
// set up test data within the transaction
}
@Test
// overrides the class-level @Commit setting
@Rollback
fun modifyDatabaseWithinTransaction() {
// logic which uses the test data and modifies database state
}
@AfterEach
fun tearDownWithinTransaction() {
// run "tear down" logic within the transaction
}
@AfterTransaction
fun verifyFinalDatabaseState() {
// logic to verify the final state after transaction has rolled back
}
}
ORM コードのテスト時に誤検知を回避する Hibernate セッションまたは JPA 永続コンテキストの状態を操作するアプリケーションコードをテストするときは、そのコードを実行するテストメソッド内の基になる作業単位を必ずフラッシュしてください。基礎となる作業単位のフラッシュに失敗すると、誤検知が発生する可能性があります。テストは成功しますが、本番環境で同じコードが例外をスローします。これは、メモリ内の作業単位を維持する ORM フレームワークに適用されることに注意してください。次の Hibernate ベースのテストケースでは、1 つのメソッドが偽陽性を示し、もう 1 つのメソッドがセッションをフラッシュした結果を正しく公開しています。 Java
Kotlin
次の例は、JPA の一致方法を示しています。 Java
Kotlin
|
3.5.10. SQL スクリプトの実行
リレーショナルデータベースに対して統合テストを作成する場合、SQL スクリプトを実行してデータベーススキーマを変更したり、テストデータをテーブルに挿入したりすることが多くの場合有益です。spring-jdbc
モジュールは、Spring ApplicationContext
がロードされたときに SQL スクリプトを実行することにより、組み込みまたは既存のデータベースを初期化するためのサポートを提供します。詳細については、組み込みデータベースのサポートおよび組み込みデータベースを使用したデータアクセスロジックのテストを参照してください。
それは ApplicationContext
がロードされたときに一度テストするためのデータベースを初期化するために非常に有用であるが、時には統合テスト中にデータベースを変更することができることが不可欠です。次のセクションでは、統合テスト中に SQL スクリプトをプログラムおよび実行により実行する方法について説明します。
プログラムによる SQL スクリプトの実行
Spring には、統合テストメソッド内でプログラムによって SQL スクリプトを実行するための以下のオプションがあります。
org.springframework.jdbc.datasource.init.ScriptUtils
org.springframework.jdbc.datasource.init.ResourceDatabasePopulator
org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests
org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests
ScriptUtils
は、SQL スクリプトを操作するための静的ユーティリティメソッドのコレクションを提供し、主にフレームワーク内での内部使用を目的としています。ただし、SQL スクリプトの解析方法と実行方法を完全に制御する必要がある場合、ScriptUtils
は、後で説明する他の代替手段よりもニーズに適している場合があります。詳細については、ScriptUtils
の個々のメソッドの javadoc を参照してください。
ResourceDatabasePopulator
は、外部リソースで定義された SQL スクリプトを使用して、プログラムでデータベースにデータを入力、初期化、またはクリーンアップするためのオブジェクトベースの API を提供します。ResourceDatabasePopulator
には、スクリプトの解析および実行時に使用される文字エンコード、ステートメント区切り文字、コメント区切り文字、エラー処理フラグを構成するためのオプションがあります。各構成オプションには、妥当なデフォルト値があります。デフォルト値の詳細については、javadoc を参照してください。ResourceDatabasePopulator
で構成されたスクリプトを実行するには、populate(Connection)
メソッドを呼び出して、java.sql.Connection
に対してポピュレーターを実行するか、execute(DataSource)
メソッドを呼び出して、javax.sql.DataSource
に対してポピュレーターを実行します。次の例では、テストスキーマとテストデータの SQL スクリプトを指定し、ステートメントセパレーターを @@
に設定し、スクリプトを DataSource
に対して実行します。
@Test
void databaseTest() {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScripts(
new ClassPathResource("test-schema.sql"),
new ClassPathResource("test-data.sql"));
populator.setSeparator("@@");
populator.execute(this.dataSource);
// run code that uses the test schema and data
}
@Test
fun databaseTest() {
val populator = ResourceDatabasePopulator()
populator.addScripts(
ClassPathResource("test-schema.sql"),
ClassPathResource("test-data.sql"))
populator.setSeparator("@@")
populator.execute(dataSource)
// run code that uses the test schema and data
}
ResourceDatabasePopulator
は、SQL スクリプトの解析と実行のために内部で ScriptUtils
に委譲することに注意してください。同様に、AbstractTransactionalJUnit4SpringContextTests
および AbstractTransactionalTestNGSpringContextTests
の executeSqlScript(..)
メソッドは、内部で ResourceDatabasePopulator
を使用して SQL スクリプトを実行します。詳細については、さまざまな executeSqlScript(..)
メソッドの Javadoc を参照してください。
@Sql を使用して SQL スクリプトを宣言的に実行する
プログラムで SQL スクリプトを実行するための前述のメカニズムに加えて、Spring TestContext フレームワークで SQL スクリプトを宣言的に構成できます。具体的には、テストクラスまたはテストメソッドで @Sql
アノテーションを宣言して、統合テストメソッドの前または後に特定のデータベースに対して実行する SQL スクリプトへの個々の SQL ステートメントまたはリソースパスを構成できます。@Sql
のサポートは SqlScriptsTestExecutionListener
によって提供され、デフォルトで有効になっています。
メソッドレベルの @Sql 宣言は、デフォルトでクラスレベルの宣言をオーバーライドします。ただし、Spring Framework 5.2, 以降、この動作は @SqlMergeMode を介してテストクラスごとまたはテストメソッドごとに構成できます。詳細については、@SqlMergeMode を使用した構成のマージとオーバーライドを参照してください。 |
パスリソースセマンティクス
各パスは Spring Resource
として解釈されます。プレーンパス("schema.sql"
など)は、テストクラスが定義されているパッケージに関連するクラスパスリソースとして扱われます。スラッシュで始まるパスは、絶対クラスパスリソースとして扱われます(たとえば、"/org/example/schema.sql"
)。URL を参照するパス(たとえば、接頭辞 classpath:
、file:
、http:
のパス)は、指定されたリソースプロトコルを使用してロードされます。
次の例は、JUnit Jupiter ベースの統合テストクラス内で、クラスレベルおよびメソッドレベルで @Sql
を使用する方法を示しています。
@SpringJUnitConfig
@Sql("/test-schema.sql")
class DatabaseTests {
@Test
void emptySchemaTest() {
// run code that uses the test schema without any test data
}
@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})
void userTest() {
// run code that uses the test schema and test data
}
}
@SpringJUnitConfig
@Sql("/test-schema.sql")
class DatabaseTests {
@Test
fun emptySchemaTest() {
// run code that uses the test schema without any test data
}
@Test
@Sql("/test-schema.sql", "/test-user-data.sql")
fun userTest() {
// run code that uses the test schema and test data
}
}
デフォルトのスクリプト検出
SQL スクリプトまたはステートメントが指定されていない場合、@Sql
が宣言されている場所に応じて、default
スクリプトの検出が試行されます。デフォルトを検出できない場合、IllegalStateException
がスローされます。
クラスレベルの宣言: アノテーション付きテストクラスが
com.example.MyTest
の場合、対応するデフォルトスクリプトはclasspath:com/example/MyTest.sql
です。メソッドレベルの宣言: アノテーション付きテストメソッドの名前が
testMethod()
で、クラスcom.example.MyTest
で定義されている場合、対応するデフォルトスクリプトはclasspath:com/example/MyTest.testMethod.sql
です。
複数の @Sql
セットの宣言
特定のテストクラスまたはテストメソッドに対して複数の SQL スクリプトセットを構成する必要があるが、構文構成、エラー処理ルール、またはセットごとに異なる実行フェーズが異なる場合、@Sql
の複数のインスタンスを宣言できます。Java 8 では、@Sql
を繰り返し可能なアノテーションとして使用できます。それ以外の場合は、@SqlGroup
アノテーションを、@Sql
の複数のインスタンスを宣言するための明示的なコンテナーとして使用できます。
次の例は、@Sql
を Java 8 で繰り返し可能なアノテーションとして使用する方法を示しています。
@Test
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`"))
@Sql("/test-user-data.sql")
void userTest() {
// run code that uses the test schema and test data
}
// Repeatable annotations with non-SOURCE retention are not yet supported by Kotlin
前の例で示したシナリオでは、test-schema.sql
スクリプトは単一行コメントに異なる構文を使用します。
次の例は、@Sql
宣言が @SqlGroup
内でグループ化されていることを除いて、前述の例と同じです。Java 8 以上では、@SqlGroup
の使用はオプションですが、Kotlin などの他の JVM 言語との互換性のために @SqlGroup
を使用する必要がある場合があります。
@Test
@SqlGroup({
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
@Sql("/test-user-data.sql")
)}
void userTest() {
// run code that uses the test schema and test data
}
@Test
@SqlGroup(
Sql("/test-schema.sql", config = SqlConfig(commentPrefix = "`")),
Sql("/test-user-data.sql"))
fun userTest() {
// Run code that uses the test schema and test data
}
スクリプト実行フェーズ
デフォルトでは、SQL スクリプトは対応するテストメソッドの前に実行されます。ただし、テストメソッドの後に特定のスクリプトセットを実行する必要がある場合(データベースの状態をクリーンアップする場合など)は、次の例に示すように、@Sql
の executionPhase
属性を使用できます。
@Test
@Sql(
scripts = "create-test-data.sql",
config = @SqlConfig(transactionMode = ISOLATED)
)
@Sql(
scripts = "delete-test-data.sql",
config = @SqlConfig(transactionMode = ISOLATED),
executionPhase = AFTER_TEST_METHOD
)
void userTest() {
// run code that needs the test data to be committed
// to the database outside of the test's transaction
}
@Test
@SqlGroup(
Sql("create-test-data.sql",
config = SqlConfig(transactionMode = ISOLATED)),
Sql("delete-test-data.sql",
config = SqlConfig(transactionMode = ISOLATED),
executionPhase = AFTER_TEST_METHOD))
fun userTest() {
// run code that needs the test data to be committed
// to the database outside of the test's transaction
}
ISOLATED
および AFTER_TEST_METHOD
は、それぞれ Sql.TransactionMode
および Sql.ExecutionPhase
から静的にインポートされることに注意してください。
@SqlConfig
を使用したスクリプト構成
@SqlConfig
アノテーションを使用して、スクリプトの解析とエラー処理を構成できます。@SqlConfig
は、統合テストクラスでクラスレベルのアノテーションとして宣言されると、テストクラス階層内のすべての SQL スクリプトのグローバル構成として機能します。@Sql
アノテーションの config
属性を使用して直接宣言された場合、@SqlConfig
は、囲む @Sql
アノテーション内で宣言された SQL スクリプトのローカル構成として機能します。@SqlConfig
のすべての属性には暗黙のデフォルト値があり、対応する属性の javadoc にドキュメント化されています。Java 言語仕様のアノテーション属性に定義されている規則のため、残念ながら、null
の値をアノテーション属性に割り当てることはできません。継承されたグローバル構成のオーバーライドをサポートするために、@SqlConfig
属性には、""
(ストリングの場合)、{}
(配列の場合)、または DEFAULT
(列挙の場合)の明示的なデフォルト値があります。このアプローチにより、@SqlConfig
のローカル宣言は、""
、{}
または DEFAULT
以外の値を提供することにより、@SqlConfig
のグローバル宣言から個々の属性を選択的にオーバーライドできます。グローバル @SqlConfig
属性は、ローカル @SqlConfig
属性が ""
、{}
または DEFAULT
以外の明示的な値を提供しない場合に常に継承されます。明示的なローカル設定はグローバル設定を上書きします。
@Sql
および @SqlConfig
によって提供される構成オプションは、ScriptUtils
および ResourceDatabasePopulator
によってサポートされるものと同等ですが、<jdbc:initialize-database/>
XML 名前空間要素によって提供される構成オプションのスーパーセットです。詳細については、@Sql
(Javadoc) および @SqlConfig
(Javadoc) の個々の属性の javadoc を参照してください。
@Sql
のトランザクション管理
デフォルトでは、SqlScriptsTestExecutionListener
は @Sql
を使用して構成されたスクリプトに必要なトランザクションセマンティクスを推測します。具体的には、SQL スクリプトは、transactionMode
属性の設定値に応じて、トランザクションなしで、既存の Spring 管理トランザクション(たとえば、@Transactional
アノテーションが付けられたテスト用の TransactionalTestExecutionListener
によって管理されるトランザクション)、または分離されたトランザクション内で実行されます。@SqlConfig
およびテストの ApplicationContext
の PlatformTransactionManager
の存在。ただし、最低限、テストの ApplicationContext
に javax.sql.DataSource
が存在する必要があります。
SqlScriptsTestExecutionListener
が DataSource
および PlatformTransactionManager
を検出してトランザクションセマンティクスを推測するために使用するアルゴリズムがニーズに合わない場合は、@SqlConfig
の dataSource
および transactionManager
属性を設定して明示的な名前を指定できます。さらに、@SqlConfig
(たとえば、スクリプトを分離されたトランザクションで実行するかどうか) の transactionMode
属性を設定することにより、トランザクション伝播の動作を制御できます。@Sql
でサポートされているすべてのトランザクション管理オプションについての詳細な説明は、このリファレンスマニュアルの範囲外ですが、@SqlConfig
(Javadoc) と SqlScriptsTestExecutionListener
(Javadoc) の javadoc には詳細な情報が記載されています。次の例は、JUnit Jupiter と @Sql
によるトランザクションテストを使用した一般的なテストシナリオを示しています。
@SpringJUnitConfig(TestDatabaseConfig.class)
@Transactional
class TransactionalSqlScriptsTests {
final JdbcTemplate jdbcTemplate;
@Autowired
TransactionalSqlScriptsTests(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
@Sql("/test-data.sql")
void usersTest() {
// verify state in test database:
assertNumUsers(2);
// run code that uses the test data...
}
int countRowsInTable(String tableName) {
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
}
void assertNumUsers(int expected) {
assertEquals(expected, countRowsInTable("user"),
"Number of rows in the [user] table.");
}
}
@SpringJUnitConfig(TestDatabaseConfig::class)
@Transactional
class TransactionalSqlScriptsTests @Autowired constructor(dataSource: DataSource) {
val jdbcTemplate: JdbcTemplate = JdbcTemplate(dataSource)
@Test
@Sql("/test-data.sql")
fun usersTest() {
// verify state in test database:
assertNumUsers(2)
// run code that uses the test data...
}
fun countRowsInTable(tableName: String): Int {
return JdbcTestUtils.countRowsInTable(jdbcTemplate, tableName)
}
fun assertNumUsers(expected: Int) {
assertEquals(expected, countRowsInTable("user"),
"Number of rows in the [user] table.")
}
}
(テストメソッド内または /test-data.sql
スクリプト内で)データベースに加えられた変更は TransactionalTestExecutionListener
によって自動的にロールバックされるため、usersTest()
メソッドの実行後にデータベースをクリーンアップする必要がないことに注意してください(詳細については、トランザクション管理を参照してください))。
@SqlMergeMode
を使用した構成のマージとオーバーライド
Spring Framework 5.2, 以降、メソッドレベルの @Sql
宣言をクラスレベルの宣言とマージすることが可能です。例:これにより、データベーススキーマの構成またはいくつかの一般的なテストデータをテストクラスごとに 1 回提供し、テストメソッドごとに追加のユースケース固有のテストデータを提供できます。@Sql
マージを有効にするには、テストクラスまたはテストメソッドに @SqlMergeMode(MERGE)
でアノテーションを付けます。特定のテストメソッド(または特定のテストサブクラス)のマージを無効にするには、@SqlMergeMode(OVERRIDE)
を使用してデフォルトモードに戻すことができます。例と詳細については、@SqlMergeMode
アノテーションドキュメントセクションを参照してください。
3.5.11. 並列テスト実行
Spring Framework 5.0 は、Spring TestContext フレームワークを使用する場合に、単一の JVM 内で並行してテストを実行するための基本的なサポートを導入しました。一般に、これは、ほとんどのテストクラスまたはテストメソッドを、テストコードや構成を変更せずに並行して実行できることを意味します。
並列テスト実行の設定方法の詳細については、テストフレームワーク、ビルドツール、または IDE のドキュメントを参照してください。 |
テストスイートに同時実行性を導入すると、予期しない副作用、奇妙なランタイム動作、テストが断続的またはランダムに失敗する可能性があることに注意してください。Spring チームは、テストを並行して実行しない場合の次の一般的なガイドラインを提供します。
次の場合は、テストを並行して実行しないでください。
Spring Framework の
@DirtiesContext
サポートを使用します。Spring Boot の
@MockBean
または@SpyBean
サポートを使用します。JUnit 4 の
@FixMethodOrder
サポート、またはテストメソッドが特定の順序で実行されるように設計されたテストフレームワーク機能を使用します。ただし、テストクラス全体が並行して実行される場合、これは当てはまりません。データベース、メッセージブローカー、ファイルシステムなどの共有サービスまたはシステムの状態を変更します。これは、組み込みシステムと外部システムの両方に適用されます。
現在のテストの これは、 |
Spring TestContext フレームワークでの並列テスト実行は、TestContext (Javadoc) の javadoc に従って、基礎となる TestContext 実装がコピーコンストラクターを提供する場合にのみ可能です。Spring で使用される DefaultTestContext は、このようなコンストラクターを提供します。ただし、カスタム TestContext 実装を提供するサードパーティライブラリを使用する場合、それが並列テストの実行に適していることを確認する必要があります。 |
3.5.12. TestContext フレームワークサポートクラス
このセクションでは、Spring TestContext フレームワークをサポートするさまざまなクラスについて説明します。
Spring JUnit 4 ランナー
Spring TestContext フレームワークは、カスタムランナー(JUnit 4.12 以上でサポート)を介して JUnit 4 と完全に統合されています。テストクラスに @RunWith(SpringJUnit4ClassRunner.class)
または短い @RunWith(SpringRunner.class)
バリアントをアノテーションすることにより、開発者は標準の JUnit 4 ベースのユニットおよび統合テストを実装し、同時にアプリケーションコンテキストのロードのサポート、テストインスタンスの依存性注入、トランザクションテストなど、TestContext フレームワークの利点を享受できます。メソッドの実行など。Spring TestContext フレームワークを代替ランナー(JUnit 4 の Parameterized
ランナーなど)またはサードパーティランナー(MockitoJUnitRunner
など)で使用する場合は、オプションで Spring の JUnit ルールのサポートを代わりに使用できます。
次のコードリストは、テストクラスをカスタム Spring Runner
で実行するように構成するための最小要件を示しています。
@RunWith(SpringRunner.class)
@TestExecutionListeners({})
public class SimpleTest {
@Test
public void testMethod() {
// test logic...
}
}
@RunWith(SpringRunner::class)
@TestExecutionListeners
class SimpleTest {
@Test
fun testMethod() {
// test logic...
}
}
上記の例では、@TestExecutionListeners
が空のリストで構成され、デフォルトのリスナーを無効にします。そうしないと、ApplicationContext
を @ContextConfiguration
で構成する必要があります。
Spring JUnit 4 ルール
org.springframework.test.context.junit4.rules
パッケージは、次の JUnit 4 ルールを提供します(JUnit 4.12 以降でサポートされます)。
SpringClassRule
SpringMethodRule
SpringClassRule
は Spring TestContext フレームワークのクラスレベルの機能をサポートする JUnit TestRule
であり、SpringMethodRule
は Spring TestContext フレームワークのインスタンスレベルおよびメソッドレベルの機能をサポートする JUnit MethodRule
です。
SpringRunner
とは対照的に、Spring のルールベースの JUnit サポートには、org.junit.runner.Runner
の実装に依存しないという利点があるため、既存の代替ランナー(JUnit 4 の Parameterized
など)またはサードパーティランナー(MockitoJUnitRunner
など)と組み合わせることができます)。
TestContext フレームワークのすべての機能をサポートするには、SpringClassRule
と SpringMethodRule
を組み合わせる必要があります。次の例は、統合テストでこれらのルールを宣言する適切な方法を示しています。
// Optionally specify a non-Spring Runner via @RunWith(...)
@ContextConfiguration
public class IntegrationTest {
@ClassRule
public static final SpringClassRule springClassRule = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@Test
public void testMethod() {
// test logic...
}
}
// Optionally specify a non-Spring Runner via @RunWith(...)
@ContextConfiguration
class IntegrationTest {
@Rule
val springMethodRule = SpringMethodRule()
@Test
fun testMethod() {
// test logic...
}
companion object {
@ClassRule
val springClassRule = SpringClassRule()
}
}
JUnit 4 サポートクラス
org.springframework.test.context.junit4
パッケージは、JUnit 4 ベースのテストケースに次のサポートクラスを提供します(JUnit 4.12 以上でサポートされます)。
AbstractJUnit4SpringContextTests
AbstractTransactionalJUnit4SpringContextTests
AbstractJUnit4SpringContextTests
は、Spring TestContext フレームワークと JUnit 4 環境での明示的な ApplicationContext
テストサポートを統合する抽象ベーステストクラスです。AbstractJUnit4SpringContextTests
を継承すると、protected
applicationContext
インスタンス変数にアクセスして、明示的な Bean ルックアップを実行したり、コンテキスト全体の状態をテストしたりできます。
AbstractTransactionalJUnit4SpringContextTests
は、AbstractJUnit4SpringContextTests
の抽象トランザクション拡張であり、JDBC アクセスに便利な機能を追加します。このクラスは、javax.sql.DataSource
Bean と PlatformTransactionManager
Bean が ApplicationContext
で定義されることを期待しています。AbstractTransactionalJUnit4SpringContextTests
を継承すると、データベースを照会する SQL ステートメントを実行するために使用できる protected
jdbcTemplate
インスタンス変数にアクセスできます。このようなクエリを使用して、データベース関連のアプリケーションコードを実行する前後にデータベースの状態を確認できます。Spring は、そのようなクエリがアプリケーションコードと同じトランザクションのスコープで実行されるようにします。ORM ツールと組み合わせて使用する場合は、必ず誤検知を避けてください。JDBC テストのサポートで記述されていたように、AbstractTransactionalJUnit4SpringContextTests
は、前述の jdbcTemplate
を使用して JdbcTestUtils
のメソッドに委譲する便利なメソッドも提供します。さらに、AbstractTransactionalJUnit4SpringContextTests
は、構成された DataSource
に対して SQL スクリプトを実行するための executeSqlScript(..)
メソッドを提供します。
これらのクラスは拡張に便利です。テストクラスを Spring 固有のクラス階層に結び付けたくない場合は、@RunWith(SpringRunner.class) または Spring の JUnit ルールを使用して独自のカスタムテストクラスを構成できます。 |
JUnit Jupiter 用 SpringExtension
Spring TestContext フレームワークは、JUnit 5 で導入された JUnit Jupiter テストフレームワークとの完全な統合を提供します。@ExtendWith(SpringExtension.class)
でテストクラスにアノテーションを付けることで、標準の JUnit Jupiter ベースのユニットテストと統合テストを実装し、同時に TestContext フレームワークのメリットを享受できます。アプリケーションコンテキストのロード、テストインスタンスの依存性注入、トランザクションテストメソッドの実行などのサポート。
さらに、JUnit Jupiter の豊富な拡張 API のおかげで、Spring は、Spring が JUnit 4 および TestNG に対してサポートする機能セットに加えて、以下の機能を提供します。
テストコンストラクター、テストメソッド、テストライフサイクルコールバックメソッドの依存性注入。詳細については、
SpringExtension
による依存性注入を参照してください。SpEL 式、環境変数、システムプロパティなどに基づく条件付きテスト実行 (英語) の強力なサポート。詳細および例については、Spring JUnit Jupiter テストアノテーションの
@EnabledIf
および@DisabledIf
の資料を参照してください。Spring と JUnit Jupiter のアノテーションを組み合わせたカスタム構成アノテーション。詳細については、テスト用のメタアノテーションのサポートの
@TransactionalDevTestConfig
および@TransactionalIntegrationTest
の例を参照してください。
次のコードリストは、SpringExtension
を @ContextConfiguration
と組み合わせて使用するようにテストクラスを構成する方法を示しています。
// Instructs JUnit Jupiter to extend the test with Spring support.
@ExtendWith(SpringExtension.class)
// Instructs Spring to load an ApplicationContext from TestConfig.class
@ContextConfiguration(classes = TestConfig.class)
class SimpleTests {
@Test
void testMethod() {
// test logic...
}
}
// Instructs JUnit Jupiter to extend the test with Spring support.
@ExtendWith(SpringExtension::class)
// Instructs Spring to load an ApplicationContext from TestConfig::class
@ContextConfiguration(classes = [TestConfig::class])
class SimpleTests {
@Test
fun testMethod() {
// test logic...
}
}
JUnit 5 のアノテーションもメタアノテーションとして使用できるため、Spring は @SpringJUnitConfig
および @SpringJUnitWebConfig
構成アノテーションを提供して、テスト ApplicationContext
および JUnit Jupiter の構成を簡素化します。
次の例では、@SpringJUnitConfig
を使用して、前の例で使用した構成の量を削減します。
// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load an ApplicationContext from TestConfig.class
@SpringJUnitConfig(TestConfig.class)
class SimpleTests {
@Test
void testMethod() {
// test logic...
}
}
// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load an ApplicationContext from TestConfig.class
@SpringJUnitConfig(TestConfig::class)
class SimpleTests {
@Test
fun testMethod() {
// test logic...
}
}
同様に、次の例では、@SpringJUnitWebConfig
を使用して、JUnit Jupiter で使用する WebApplicationContext
を作成します。
// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load a WebApplicationContext from TestWebConfig.class
@SpringJUnitWebConfig(TestWebConfig.class)
class SimpleWebTests {
@Test
void testMethod() {
// test logic...
}
}
// Instructs Spring to register the SpringExtension with JUnit
// Jupiter and load a WebApplicationContext from TestWebConfig::class
@SpringJUnitWebConfig(TestWebConfig::class)
class SimpleWebTests {
@Test
fun testMethod() {
// test logic...
}
}
詳細については、Spring JUnit Jupiter テストアノテーションの @SpringJUnitConfig
および @SpringJUnitWebConfig
の資料を参照してください。
SpringExtension
による依存性注入
SpringExtension
は、JUnit Jupiter の ParameterResolver
(英語) 拡張 API を実装します。これにより、Spring は、テストコンストラクター、テストメソッド、テストライフサイクルコールバックメソッドに依存性注入を提供できます。
具体的には、SpringExtension
は、テストの ApplicationContext
から @BeforeAll
、@AfterAll
、@BeforeEach
、@AfterEach
、@Test
、@RepeatedTest
、@ParameterizedTest
などのアノテーションが付けられたテストコンストラクターおよびメソッドに依存関係を注入できます。
コンストラクターインジェクション
JUnit Jupiter テストクラスのコンストラクターの特定のパラメーターがタイプ ApplicationContext
(またはそのサブタイプ)であるか、または @Autowired
、@Qualifier
、または @Value
でアノテーションまたはメタアノテーションが付けられている場合、Spring はその特定のパラメーターの値に対応する Bean または値を挿入します。テストの ApplicationContext
から。
Spring は、コンストラクターが autowirable であると見なされる場合、テストクラスコンストラクターのすべての引数をオートワイヤーするように構成することもできます。コンストラクターは、次の条件のいずれかが(優先順位の順に)満たされる場合、自動書き込み可能と見なされます。
コンストラクターには
@Autowired
のアノテーションが付けられます。@TestConstructor
は、autowireMode
属性がALL
に設定されたテストクラスに存在またはメタ存在します。デフォルトのテストコンストラクターのオートワイヤーモードは
ALL
に変更されました。
@TestConstructor
の使用と、グローバルテストコンストラクターのオートワイヤーモードを変更する方法の詳細については、@TestConstructor
を参照してください。
テストクラスのコンストラクターが autowirable であると見なされる場合、Spring はコンストラクター内のすべてのパラメーターの引数を解決する責任を負います。その結果、JUnit Jupiter に登録されている他の ParameterResolver は、そのようなコンストラクターのパラメーターを解決できません。 |
その理由は、
|
次の例では、Spring は、TestConfig.class
から OrderServiceIntegrationTests
コンストラクターにロードされた ApplicationContext
から OrderService
Bean を注入します。
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
private final OrderService orderService;
@Autowired
OrderServiceIntegrationTests(OrderService orderService) {
this.orderService = orderService;
}
// tests that use the injected OrderService
}
@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests @Autowired constructor(private val orderService: OrderService){
// tests that use the injected OrderService
}
この機能により、テストの依存関係が final
になり、不変になります。
spring.test.constructor.autowire.mode
プロパティが all
の場合(@TestConstructor
を参照)、前の例のコンストラクターでの @Autowired
の宣言を省略して、次のようにすることができます。
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
private final OrderService orderService;
OrderServiceIntegrationTests(OrderService orderService) {
this.orderService = orderService;
}
// tests that use the injected OrderService
}
@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests(val orderService:OrderService) {
// tests that use the injected OrderService
}
メソッドインジェクション
JUnit Jupiter テストメソッドまたはテストライフサイクルコールバックメソッドのパラメーターが ApplicationContext
タイプ(またはそのサブタイプ)であるか、または @Autowired
、@Qualifier
、または @Value
でアノテーションまたはメタアノテーションが付けられている場合、Spring は対応する Bean でその特定のパラメーターの値を注入します。テストの ApplicationContext
次の例では、Spring は TestConfig.class
から deleteOrder()
テストメソッドにロードされた ApplicationContext
から OrderService
を挿入します。
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
@Test
void deleteOrder(@Autowired OrderService orderService) {
// use orderService from the test's ApplicationContext
}
}
@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests {
@Test
fun deleteOrder(@Autowired orderService: OrderService) {
// use orderService from the test's ApplicationContext
}
}
JUnit Jupiter の ParameterResolver
サポートの堅牢性により、Spring だけでなく、JUnit Jupiter 自体または他のサードパーティの拡張機能からも、単一のメソッドに複数の依存関係を注入できます。
次の例は、Spring と JUnit Jupiter の両方で placeOrderRepeatedly()
テストメソッドに依存関係を同時に注入する方法を示しています。
@SpringJUnitConfig(TestConfig.class)
class OrderServiceIntegrationTests {
@RepeatedTest(10)
void placeOrderRepeatedly(RepetitionInfo repetitionInfo,
@Autowired OrderService orderService) {
// use orderService from the test's ApplicationContext
// and repetitionInfo from JUnit Jupiter
}
}
@SpringJUnitConfig(TestConfig::class)
class OrderServiceIntegrationTests {
@RepeatedTest(10)
fun placeOrderRepeatedly(repetitionInfo:RepetitionInfo, @Autowired orderService:OrderService) {
// use orderService from the test's ApplicationContext
// and repetitionInfo from JUnit Jupiter
}
}
JUnit Jupiter の @RepeatedTest
を使用すると、テストメソッドが RepetitionInfo
にアクセスできることに注意してください。
@Nested
テストクラスの構成
Spring TestContext フレームワークは、Spring Framework 5.0; 以降、JUnit Jupiter の @Nested
テストクラスでのテスト関連アノテーションの使用をサポートしていますが、Spring Framework 5.3 クラスレベルのテスト構成アノテーションは、スーパークラスからのようにクラスを囲むことから継承されませんでした。
Spring Framework 5.3 は、包含クラスからテストクラス構成を継承するためのファーストクラスのサポートを導入し、そのような構成はデフォルトで継承されます。デフォルトの INHERIT
モードから OVERRIDE
モードに変更するには、個々の @Nested
テストクラスに @NestedTestConfiguration(EnclosingConfiguration.OVERRIDE)
のアノテーションを付けることができます。明示的な @NestedTestConfiguration
宣言は、アノテーション付きのテストクラスと、そのサブクラスおよびネストされたクラスのいずれかに適用されます。トップレベルのテストクラスに @NestedTestConfiguration
のアノテーションを付けることができます。これは、ネストされたすべてのテストクラスに再帰的に適用されます。
開発チームがデフォルトを OVERRIDE
に変更できるようにするために(たとえば、Spring Framework 5.0 から 5.2 との互換性のために)、デフォルトモードは、JVM システムプロパティまたはクラスパスのルートにある spring.properties
ファイルを介してグローバルに変更できます。詳細については、「デフォルトの包含構成継承モードの変更」ノートを参照してください。
次の「HelloWorld」の例は非常に単純ですが、@Nested
テストクラスによって継承されるトップレベルクラスで共通の構成を宣言する方法を示しています。この特定の例では、TestConfig
構成クラスのみが継承されます。ネストされたテストクラスごとに独自のアクティブプロファイルのセットが提供されるため、ネストされたテストクラスごとに個別の ApplicationContext
が生成されます(詳細については、コンテキストキャッシングを参照してください)。サポートされているアノテーションのリストを参照して、@Nested
テストクラスで継承できるアノテーションを確認してください。
@SpringJUnitConfig(TestConfig.class)
class GreetingServiceTests {
@Nested
@ActiveProfiles("lang_en")
class EnglishGreetings {
@Test
void hello(@Autowired GreetingService service) {
assertThat(service.greetWorld()).isEqualTo("Hello World");
}
}
@Nested
@ActiveProfiles("lang_de")
class GermanGreetings {
@Test
void hello(@Autowired GreetingService service) {
assertThat(service.greetWorld()).isEqualTo("Hallo Welt");
}
}
}
@SpringJUnitConfig(TestConfig::class)
class GreetingServiceTests {
@Nested
@ActiveProfiles("lang_en")
inner class EnglishGreetings {
@Test
fun hello(@Autowired service:GreetingService) {
assertThat(service.greetWorld()).isEqualTo("Hello World")
}
}
@Nested
@ActiveProfiles("lang_de")
inner class GermanGreetings {
@Test
fun hello(@Autowired service:GreetingService) {
assertThat(service.greetWorld()).isEqualTo("Hallo Welt")
}
}
}
TestNG サポートクラス
org.springframework.test.context.testng
パッケージは、TestNG ベースのテストケースに対して以下のサポートクラスを提供します。
AbstractTestNGSpringContextTests
AbstractTransactionalTestNGSpringContextTests
AbstractTestNGSpringContextTests
は、Spring TestContext フレームワークを TestNG 環境での明示的な ApplicationContext
テストサポートと統合する抽象基本テストクラスです。AbstractTestNGSpringContextTests
を継承すると、protected
applicationContext
インスタンス変数にアクセスして、明示的な Bean ルックアップを実行したり、コンテキスト全体の状態をテストしたりできます。
AbstractTransactionalTestNGSpringContextTests
は、AbstractTestNGSpringContextTests
の抽象トランザクション拡張であり、JDBC アクセスに便利な機能を追加します。このクラスは、javax.sql.DataSource
Bean と PlatformTransactionManager
Bean が ApplicationContext
で定義されることを期待しています。AbstractTransactionalTestNGSpringContextTests
を継承すると、データベースを照会する SQL ステートメントを実行するために使用できる protected
jdbcTemplate
インスタンス変数にアクセスできます。このようなクエリを使用して、データベース関連のアプリケーションコードを実行する前後にデータベースの状態を確認できます。Spring は、そのようなクエリがアプリケーションコードと同じトランザクションのスコープで実行されるようにします。ORM ツールと組み合わせて使用する場合は、必ず誤検知を避けてください。JDBC テストのサポートで記述されていたように、AbstractTransactionalTestNGSpringContextTests
は、前述の jdbcTemplate
を使用して JdbcTestUtils
のメソッドに委譲する便利なメソッドも提供します。さらに、AbstractTransactionalTestNGSpringContextTests
は、構成された DataSource
に対して SQL スクリプトを実行するための executeSqlScript(..)
メソッドを提供します。
これらのクラスは拡張に便利です。テストクラスを Spring 固有のクラス階層に結び付けたくない場合は、@ContextConfiguration 、@TestExecutionListeners などを使用するか、TestContextManager を使用してテストクラスを手動でインストルメントすることにより、独自のカスタムテストクラスを構成できます。テストクラスをインスツルメントする方法の例については、AbstractTestNGSpringContextTests のソースコードを参照してください。 |
3.6. WebTestClient
WebTestClient
は、サーバーアプリケーションをテストするために設計された HTTP クライアントです。Spring の WebClient をラップし、それを使用してリクエストを実行しますが、レスポンスを検証するためのテストファサードを公開します。WebTestClient
は、エンドツーエンドの HTTP テストを実行するために使用できます。また、モックサーバーのリクエストおよびレスポンスオブジェクトを介して、サーバーを実行せずに SpringMVC および Spring WebFlux アプリケーションをテストするために使用することもできます。
Kotlin ユーザー: WebTestClient の使用に関連するこのセクションを参照してください。 |
3.6.1. セットアップ
WebTestClient
をセットアップするには、バインドするサーバーセットアップを選択する必要があります。これは、いくつかのモックサーバーセットアップの選択肢の 1 つ、またはライブサーバーへの接続のいずれかです。
コントローラーにバインド
この設定により、サーバーを実行せずに、モックリクエストおよびレスポンスオブジェクトを介して特定のコントローラーをテストできます。
WebFlux アプリケーションの場合、WebFlux Java 構成と同等のインフラストラクチャをロードし、指定されたコントローラーを登録し、リクエストを処理するために WebHandler チェーンを作成する以下を使用します。
WebTestClient client =
WebTestClient.bindToController(new TestController()).build();
val client = WebTestClient.bindToController(TestController()).build()
Spring MVC の場合、以下を使用して StandaloneMockMvcBuilder (Javadoc) に委譲し、WebMvc Java 構成と同等のインフラストラクチャをロードし、指定されたコントローラーを登録し、リクエストを処理するための MockMvc のインスタンスを作成します。
WebTestClient client =
MockMvcWebTestClient.bindToController(new TestController()).build();
val client = MockMvcWebTestClient.bindToController(TestController()).build()
ApplicationContext
にバインド
このセットアップにより、SpringMVC または Spring WebFlux インフラストラクチャーとコントローラー宣言を使用して Spring 構成をロードし、それを使用して、サーバーを実行せずに、モックリクエストおよびレスポンスオブジェクトを介してリクエストを処理できます。
WebFlux の場合、Spring ApplicationContext
が WebHttpHandlerBuilder (Javadoc) に渡される場所で以下を使用して、リクエストを処理する WebHandler チェーンを作成します。
@SpringJUnitConfig(WebConfig.class) (1)
class MyTests {
WebTestClient client;
@BeforeEach
void setUp(ApplicationContext context) { (2)
client = WebTestClient.bindToApplicationContext(context).build(); (3)
}
}
1 | ロードする構成を指定する |
2 | 構成を注入する |
3 | WebTestClient を作成する |
@SpringJUnitConfig(WebConfig::class) (1)
class MyTests {
lateinit var client: WebTestClient
@BeforeEach
fun setUp(context: ApplicationContext) { (2)
client = WebTestClient.bindToApplicationContext(context).build() (3)
}
}
1 | ロードする構成を指定する |
2 | 構成を注入する |
3 | WebTestClient を作成する |
Spring MVC の場合、Spring ApplicationContext
が MockMvcBuilders.webAppContextSetup (Javadoc) に渡される場合は、以下を使用して、リクエストを処理する MockMvc インスタンスを作成します。
@ExtendWith(SpringExtension.class)
@WebAppConfiguration("classpath:META-INF/web-resources") (1)
@ContextHierarchy({
@ContextConfiguration(classes = RootConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
class MyTests {
@Autowired
WebApplicationContext wac; (2)
WebTestClient client;
@BeforeEach
void setUp() {
client = MockMvcWebTestClient.bindToApplicationContext(this.wac).build(); (3)
}
}
1 | ロードする構成を指定する |
2 | 構成を注入する |
3 | WebTestClient を作成する |
@ExtendWith(SpringExtension.class)
@WebAppConfiguration("classpath:META-INF/web-resources") (1)
@ContextHierarchy({
@ContextConfiguration(classes = RootConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
class MyTests {
@Autowired
lateinit var wac: WebApplicationContext; (2)
lateinit var client: WebTestClient
@BeforeEach
fun setUp() { (2)
client = MockMvcWebTestClient.bindToApplicationContext(wac).build() (3)
}
}
1 | ロードする構成を指定する |
2 | 構成を注入する |
3 | WebTestClient を作成する |
ルーター関数にバインド
この設定により、サーバーを実行せずに、モックリクエストおよびレスポンスオブジェクトを介して関数エンドポイントをテストできます。
WebFlux の場合、RouterFunctions.toWebHandler
に委譲する以下を使用して、リクエストを処理するサーバーセットアップを作成します。
RouterFunction<?> route = ...
client = WebTestClient.bindToRouterFunction(route).build();
val route: RouterFunction<*> = ...
val client = WebTestClient.bindToRouterFunction(route).build()
Spring MVC の場合、現在 WebMvc 関数エンドポイントをテストするオプションはありません。
サーバーにバインド
このセットアップは、実行中のサーバーに接続して、完全なエンドツーエンドの HTTP テストを実行します。
client = WebTestClient.bindToServer().baseUrl("http://localhost:8080").build();
client = WebTestClient.bindToServer().baseUrl("http://localhost:8080").build()
クライアント構成
前述のサーバーセットアップオプションに加えて、ベース URL、デフォルトヘッダー、クライアントフィルターなどのクライアントオプションを構成することもできます。これらのオプションは、bindToServer()
に続いてすぐに利用できます。他のすべての構成オプションについては、次のように configureClient()
を使用してサーバー構成からクライアント構成に移行する必要があります。
client = WebTestClient.bindToController(new TestController())
.configureClient()
.baseUrl("/test")
.build();
client = WebTestClient.bindToController(TestController())
.configureClient()
.baseUrl("/test")
.build()
3.6.2. テストの作成
WebTestClient
は、exchange()
を使用してリクエストを実行するまで、WebClient と同じ API を提供します。フォームデータ、マルチパートデータなどのコンテンツを含むリクエストを準備する方法の例については、WebClient のドキュメントを参照してください。
exchange()
の呼び出し後、WebTestClient
は WebClient
から分岐し、代わりにワークフローを続行してレスポンスを確認します。
レスポンスステータスとヘッダーをアサートするには、以下を使用します。
client.get().uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
client.get().uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
次に、次のいずれかを使用してレスポンス本文をデコードすることを選択できます。
expectBody(Class<T>)
: 単一のオブジェクトにデコードします。expectBodyList(Class<T>)
: オブジェクトをデコードしてList<T>
に収集します。expectBody()
: JSON コンテンツまたは空のボディの場合はbyte[]
にデコードします。
そして、結果のより高いレベルのオブジェクトに対してアサーションを実行します。
client.get().uri("/persons")
.exchange()
.expectStatus().isOk()
.expectBodyList(Person.class).hasSize(3).contains(person);
import org.springframework.test.web.reactive.server.expectBodyList
client.get().uri("/persons")
.exchange()
.expectStatus().isOk()
.expectBodyList<Person>().hasSize(3).contains(person)
組み込みのアサーションが不十分な場合は、代わりにオブジェクトを使用して、他のアサーションを実行できます。
import org.springframework.test.web.reactive.server.expectBody
client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody(Person.class)
.consumeWith(result -> {
// custom assertions (e.g. AssertJ)...
});
client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody<Person>()
.consumeWith {
// custom assertions (e.g. AssertJ)...
}
または、ワークフローを終了して EntityExchangeResult
を取得することもできます。
EntityExchangeResult<Person> result = client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody(Person.class)
.returnResult();
import org.springframework.test.web.reactive.server.expectBody
val result = client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk
.expectBody<Person>()
.returnResult()
ジェネリックを使用してターゲットタイプにデコードする必要がある場合は、Class<T> ではなく ParameterizedTypeReference (Javadoc) を受け入れるオーバーロードメソッドを探します。 |
コンテンツなし
レスポンスに内容が含まれることが予想されない場合は、次のように主張できます。
client.post().uri("/persons")
.body(personMono, Person.class)
.exchange()
.expectStatus().isCreated()
.expectBody().isEmpty();
client.post().uri("/persons")
.bodyValue(person)
.exchange()
.expectStatus().isCreated()
.expectBody().isEmpty()
レスポンスコンテンツを無視する場合、以下はアサーションなしでコンテンツをリリースします。
client.get().uri("/persons/123")
.exchange()
.expectStatus().isNotFound()
.expectBody(Void.class);
client.get().uri("/persons/123")
.exchange()
.expectStatus().isNotFound
.expectBody<Unit>()
JSON コンテンツ
ターゲットタイプなしで expectBody()
を使用して、高レベルのオブジェクトを介してではなく、生のコンテンツに対してアサーションを実行できます。
JSONAssert (英語) で完全な JSON コンテンツを確認するには:
client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody()
.json("{\"name\":\"Jane\"}")
client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody()
.json("{\"name\":\"Jane\"}")
JSONPath: GitHub (英語) で JSON コンテンツを確認するには:
client.get().uri("/persons")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$[0].name").isEqualTo("Jane")
.jsonPath("$[1].name").isEqualTo("Jason");
client.get().uri("/persons")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$[0].name").isEqualTo("Jane")
.jsonPath("$[1].name").isEqualTo("Jason")
ストリーミングレスポンス
"text/event-stream"
や "application/x-ndjson"
などの潜在的に無限のストリームをテストするには、レスポンスステータスとヘッダーを確認することから始めて、次に FluxExchangeResult
を取得します。
FluxExchangeResult<MyEvent> result = client.get().uri("/events")
.accept(TEXT_EVENT_STREAM)
.exchange()
.expectStatus().isOk()
.returnResult(MyEvent.class);
import org.springframework.test.web.reactive.server.returnResult
val result = client.get().uri("/events")
.accept(TEXT_EVENT_STREAM)
.exchange()
.expectStatus().isOk()
.returnResult<MyEvent>()
これで、reactor-test
から StepVerifier
を使用してレスポンスストリームを使用する準備が整いました。
Flux<Event> eventFlux = result.getResponseBody();
StepVerifier.create(eventFlux)
.expectNext(person)
.expectNextCount(4)
.consumeNextWith(p -> ...)
.thenCancel()
.verify();
val eventFlux = result.getResponseBody()
StepVerifier.create(eventFlux)
.expectNext(person)
.expectNextCount(4)
.consumeNextWith { p -> ... }
.thenCancel()
.verify()
MockMvc アサーション
WebTestClient
は HTTP クライアントであるため、ステータス、ヘッダー、本文など、クライアントのレスポンスの内容のみを確認できます。
MockMvc サーバーセットアップを使用して SpringMVC アプリケーションをテストする場合、サーバーレスポンスに対してさらにアサーションを実行するための追加の選択肢があります。これを行うには、本体をアサートした後に ExchangeResult
を取得することから始めます。
// For a response with a body
EntityExchangeResult<Person> result = client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody(Person.class)
.returnResult();
// For a response without a body
EntityExchangeResult<Void> result = client.get().uri("/path")
.exchange()
.expectBody().isEmpty();
// For a response with a body
val result = client.get().uri("/persons/1")
.exchange()
.expectStatus().isOk()
.expectBody(Person.class)
.returnResult();
// For a response without a body
val result = client.get().uri("/path")
.exchange()
.expectBody().isEmpty();
次に、MockMvc サーバーレスポンスアサーションに切り替えます。
MockMvcWebTestClient.resultActionsFor(result)
.andExpect(model().attribute("integer", 3))
.andExpect(model().attribute("string", "a string value"));
MockMvcWebTestClient.resultActionsFor(result)
.andExpect(model().attribute("integer", 3))
.andExpect(model().attribute("string", "a string value"));
3.7. MockMvc
MockMvc とも呼ばれる SpringMVC テストフレームワークは、SpringMVC アプリケーションのテストをサポートします。完全な SpringMVC リクエスト処理を実行しますが、実行中のサーバーではなく、モックリクエストおよびレスポンスオブジェクトを介して実行します。
MockMvc を単独で使用して、リクエストを実行し、レスポンスを検証できます。また、MockMvc がリクエストを処理するサーバーとしてプラグインされている WebTestClient を介して使用することもできます。WebTestClient
の利点は、生データの代わりに高レベルのオブジェクトを操作するオプションと、ライブサーバーに対して完全なエンドツーエンドの HTTP テストに切り替えて、同じテスト API を使用できることです。
3.7.1. 概要
コントローラーをインスタンス化し、依存関係を挿入し、そのメソッドを呼び出すことで、SpringMVC のプレーンな単体テストを記述できます。ただし、このようなテストでは、リクエストのマッピング、データバインディング、メッセージ変換、型変換、検証は検証されません。また、サポートされている @InitBinder
、@ModelAttribute
または @ExceptionHandler
メソッドも含まれません。
MockMvc
としても知られる SpringMVC テストフレームワークは、サーバーを実行せずに SpringMVC コントローラーのより完全なテストを提供することを目的としています。これは、DispacherServlet
を呼び出し、サーバーを実行せずに完全な SpringMVC リクエスト処理を複製する spring-test
モジュールからサーブレット API の「モック」実装を渡すことによって行われます。
MockMvc は、軽量でターゲットを絞ったテストを使用して SpringMVC アプリケーションのほとんどの機能を検証できるサーバー側のテストフレームワークです。これを単独で使用してリクエストを実行し、レスポンスを検証することも、MockMvc をサーバーとしてプラグインしてリクエストを処理する WebTestClientAPI を介して使用することもできます。
静的インポート
MockMvc を直接使用してリクエストを実行する場合は、次の静的インポートが必要です。
MockMvcBuilders.*
MockMvcRequestBuilders.*
MockMvcResultMatchers.*
MockMvcResultHandlers.*
それを覚える簡単な方法は、MockMvc*
を検索することです。Eclipse を使用する場合は、Eclipse 設定で上記を「お気に入りの静的メンバー」として追加してください。
WebTestClient を介して MockMvc を使用する場合、静的インポートは必要ありません。WebTestClient
は、静的インポートなしで流暢な API を提供します。
セットアップの選択
MockMvc は、2 つの方法のいずれかでセットアップできます。1 つは、テストするコントローラーを直接ポイントし、SpringMVC インフラストラクチャーをプログラムで構成することです。2 つ目は、SpringMVC とコントローラーインフラストラクチャーを含む Spring 構成を指すことです。
特定のコントローラーをテストするために MockMvc をセットアップするには、以下を使用します。
class MyWebTests {
MockMvc mockMvc;
@BeforeEach
void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
}
// ...
}
class MyWebTests {
lateinit var mockMvc : MockMvc
@BeforeEach
fun setup() {
mockMvc = MockMvcBuilders.standaloneSetup(AccountController()).build()
}
// ...
}
または、上記と同じビルダーに委譲する WebTestClient を介してテストするときに、このセットアップを使用することもできます。
Spring 構成を介して MockMvc をセットアップするには、以下を使用します。
@SpringJUnitWebConfig(locations = "my-servlet-context.xml")
class MyWebTests {
MockMvc mockMvc;
@BeforeEach
void setup(WebApplicationContext wac) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
// ...
}
@SpringJUnitWebConfig(locations = ["my-servlet-context.xml"])
class MyWebTests {
lateinit var mockMvc: MockMvc
@BeforeEach
fun setup(wac: WebApplicationContext) {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build()
}
// ...
}
または、上記と同じビルダーに委譲する WebTestClient を介してテストするときに、このセットアップを使用することもできます。
どのセットアップオプションを使用する必要がありますか?
webAppContextSetup
は、実際の Spring MVC 構成をロードし、より完全な統合テストを実施します。TestContext フレームワークはロードされた Spring 構成をキャッシュするため、テストスイートにさらに多くのテストを導入する場合でも、テストを高速に実行し続けるのに役立ちます。さらに、Spring 構成を通じてモックサービスをコントローラーに注入して、Web レイヤーのテストに集中することができます。次の例では、Mockito でモックサービスを宣言しています。
<bean id="accountService" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.example.AccountService"/>
</bean>
次に、次の例に示すように、モックサービスをテストに挿入して、期待を設定および検証できます。
@SpringJUnitWebConfig(locations = "test-servlet-context.xml")
class AccountTests {
@Autowired
AccountService accountService;
MockMvc mockMvc;
@BeforeEach
void setup(WebApplicationContext wac) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
// ...
}
@SpringJUnitWebConfig(locations = ["test-servlet-context.xml"])
class AccountTests {
@Autowired
lateinit var accountService: AccountService
lateinit mockMvc: MockMvc
@BeforeEach
fun setup(wac: WebApplicationContext) {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build()
}
// ...
}
一方、standaloneSetup
は、ユニットテストに少し近づいています。一度に 1 つのコントローラーをテストします。コントローラーにモック依存関係を手動で挿入できます。Spring 構成のロードは必要ありません。このようなテストはスタイルに重点を置いており、どのコントローラーがテストされているか、特定の Spring MVC 構成が動作するために必要かどうかなどを簡単に確認できます。standaloneSetup
は、特定の動作を検証したり課題をデバッグしたりするためのアドホックテストを作成する非常に便利な方法でもあります。
ほとんどの「統合と単体テスト」の議論と同様に、正解も不正解もありません。ただし、standaloneSetup
を使用すると、Spring MVC 構成を検証するために、webAppContextSetup
テストを追加する必要があります。または、常に実際の Spring MVC 構成に対してテストするために、すべてのテストを webAppContextSetup
で作成できます。
セットアップ機能
使用する MockMvc ビルダーに関係なく、すべての MockMvcBuilder
実装は、いくつかの一般的で非常に便利な機能を提供します。例:次のように、すべてのリクエストに対して Accept
ヘッダーを宣言し、すべてのレスポンスに Content-Type
ヘッダーと同様に 200 のステータスを期待できます。
// static import of MockMvcBuilders.standaloneSetup
MockMvc mockMvc = standaloneSetup(new MusicController())
.defaultRequest(get("/").accept(MediaType.APPLICATION_JSON))
.alwaysExpect(status().isOk())
.alwaysExpect(content().contentType("application/json;charset=UTF-8"))
.build();
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
さらに、サードパーティのフレームワーク(およびアプリケーション)は、MockMvcConfigurer
にあるようなセットアップ手順を事前にパッケージ化できます。Spring Framework には、リクエスト間で HTTP セッションを保存および再利用するのに役立つ組み込み実装が 1 つあります。次のように使用できます。
// static import of SharedHttpSessionConfigurer.sharedHttpSession
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TestController())
.apply(sharedHttpSession())
.build();
// Use mockMvc to perform requests...
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
すべての MockMvc ビルダー機能のリストについては、ConfigurableMockMvcBuilder
(Javadoc) の javadoc を参照するか、IDE を使用して使用可能なオプションを調べましょう。
リクエストの実行
このセクションでは、MockMvc を単独で使用して、リクエストを実行し、レスポンスを検証する方法を示します。WebTestClient
を介して MockMvc を使用する場合は、代わりにテストの作成の対応するセクションを参照してください。
次の例に示すように、任意の HTTP メソッドを使用するリクエストを実行するには:
// static import of MockMvcRequestBuilders.*
mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));
import org.springframework.test.web.servlet.post
mockMvc.post("/hotels/{id}", 42) {
accept = MediaType.APPLICATION_JSON
}
内部で MockMultipartHttpServletRequest
を使用するファイルアップロードリクエストを実行して、マルチパートリクエストの実際の解析が行われないようにすることもできます。むしろ、次の例のように設定する必要があります。
mockMvc.perform(multipart("/doc").file("a1", "ABC".getBytes("UTF-8")));
import org.springframework.test.web.servlet.multipart
mockMvc.multipart("/doc") {
file("a1", "ABC".toByteArray(charset("UTF8")))
}
次の例に示すように、クエリテンプレートを URI テンプレートスタイルで指定できます。
mockMvc.perform(get("/hotels?thing={thing}", "somewhere"));
mockMvc.get("/hotels?thing={thing}", "somewhere")
次の例に示すように、クエリまたはフォームパラメーターを表すサーブレットリクエストパラメーターを追加することもできます。
mockMvc.perform(get("/hotels").param("thing", "somewhere"));
import org.springframework.test.web.servlet.get
mockMvc.get("/hotels") {
param("thing", "somewhere")
}
アプリケーションコードがサーブレットリクエストパラメーターに依存しており、クエリ文字列を明示的にチェックしない場合(ほとんどの場合)、使用するオプションは関係ありません。ただし、URI テンプレートで提供されるクエリパラメーターはデコードされますが、param(…)
メソッドを介して提供されるリクエストパラメーターはすでにデコードされていることが予想されます。
ほとんどの場合、コンテキスト URI とサーブレットパスはリクエスト URI から除外することをお勧めします。完全なリクエスト URI でテストする必要がある場合は、次の例に示すように、リクエストマッピングが機能するように、contextPath
と servletPath
を必ず設定してください。
mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main"))
import org.springframework.test.web.servlet.get
mockMvc.get("/app/main/hotels/{id}") {
contextPath = "/app"
servletPath = "/main"
}
上記の例では、実行されたすべてのリクエストで contextPath
および servletPath
を設定するのは面倒です。代わりに、次の例に示すように、デフォルトのリクエストプロパティを設定できます。
class MyWebTests {
MockMvc mockMvc;
@BeforeEach
void setup() {
mockMvc = standaloneSetup(new AccountController())
.defaultRequest(get("/")
.contextPath("/app").servletPath("/main")
.accept(MediaType.APPLICATION_JSON)).build();
}
}
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
上記のプロパティは、MockMvc
インスタンスを介して実行されるすべてのリクエストに影響します。特定のリクエストで同じプロパティが指定されている場合、デフォルト値が上書きされます。そのため、デフォルトのリクエストの HTTP メソッドと URI は重要ではありません。リクエストごとに指定する必要があるためです。
期待の定義
次の例に示すように、リクエストの実行後に 1 つ以上の .andExpect(..)
呼び出しを追加することにより、期待を定義できます。
// static import of MockMvcRequestBuilders.* and MockMvcResultMatchers.*
mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());
import org.springframework.test.web.servlet.get
mockMvc.get("/accounts/1").andExpect {
status().isOk()
}
MockMvcResultMatchers.*
は多くの期待を提供しますが、その中にはさらに入れ子になってさらに詳細な期待があります。
期待は 2 つの一般的なカテゴリに分類されます。アサーションの最初のカテゴリは、レスポンスのプロパティ(レスポンスステータス、ヘッダー、コンテンツなど)を検証します。これらは、主張する最も重要な結果です。
アサーションの 2 番目のカテゴリは、レスポンスを超えています。これらのアサーションにより、どのコントローラーメソッドがリクエストを処理したか、例外が発生して処理されたかどうか、モデルの内容、選択されたビュー、追加されたフラッシュ属性など、Spring MVC 固有の側面をインスペクションできます。また、リクエストやセッション属性など、サーブレット固有の側面をインスペクションできます。
次のテストは、バインディングまたは検証が失敗したことを表明します。
mockMvc.perform(post("/persons"))
.andExpect(status().isOk())
.andExpect(model().attributeHasErrors("person"));
import org.springframework.test.web.servlet.post
mockMvc.post("/persons").andExpect {
status().isOk()
model {
attributeHasErrors("person")
}
}
多くの場合、テストを作成するとき、実行されたリクエストの結果をダンプすることが有用です。print()
は MockMvcResultHandlers
からの静的インポートです。
mockMvc.perform(post("/persons"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(model().attributeHasErrors("person"));
import org.springframework.test.web.servlet.post
mockMvc.post("/persons").andDo {
print()
}.andExpect {
status().isOk()
model {
attributeHasErrors("person")
}
}
リクエスト処理によって未処理の例外が発生しない限り、print()
メソッドは、利用可能なすべての結果データを System.out
に出力します。log()
メソッドと print()
メソッドの 2 つの追加バリアントもあります。1 つは OutputStream
を受け入れ、もう 1 つは Writer
を受け入れます。例: print(System.err)
を呼び出すと結果データが System.err
に出力され、print(myWriter)
を呼び出すと結果データがカスタムライターに出力されます。結果データを出力する代わりにログに記録する場合は、log()
メソッドを呼び出して、結果データを org.springframework.test.web.servlet.result
ロギングカテゴリに単一の DEBUG
メッセージとして記録します。
場合によっては、結果に直接アクセスして、他の方法では検証できないものを検証したい場合があります。これは、次の例に示すように、他のすべての期待値の後に .andReturn()
を追加することで実現できます。
MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn();
// ...
var mvcResult = mockMvc.post("/persons").andExpect { status().isOk() }.andReturn()
// ...
すべてのテストが同じ期待値を繰り返す場合、次の例に示すように、MockMvc
インスタンスを構築するときに共通の期待値を 1 回設定できます。
standaloneSetup(new SimpleController())
.alwaysExpect(status().isOk())
.alwaysExpect(content().contentType("application/json;charset=UTF-8"))
.build()
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
共通の期待は常に適用され、別の MockMvc
インスタンスを作成せずに上書きすることはできません。
JSON レスポンスコンテンツに Spring HATEOAS: GitHub (英語) で作成されたハイパーメディアリンクが含まれる場合、次の例に示すように、JsonPath 式を使用して、結果のリンクを確認できます。
mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.links[?(@.rel == 'self')].href").value("http://localhost:8080/people"));
mockMvc.get("/people") {
accept(MediaType.APPLICATION_JSON)
}.andExpect {
jsonPath("$.links[?(@.rel == 'self')].href") {
value("http://localhost:8080/people")
}
}
XML レスポンスコンテンツに Spring HATEOAS: GitHub (英語) で作成されたハイパーメディアリンクが含まれている場合、XPath 式を使用して、結果のリンクを確認できます。
Map<String, String> ns = Collections.singletonMap("ns", "http://www.w3.org/2005/Atom");
mockMvc.perform(get("/handle").accept(MediaType.APPLICATION_XML))
.andExpect(xpath("/person/ns:link[@rel='self']/@href", ns).string("http://localhost:8080/people"));
val ns = mapOf("ns" to "http://www.w3.org/2005/Atom")
mockMvc.get("/handle") {
accept(MediaType.APPLICATION_XML)
}.andExpect {
xpath("/person/ns:link[@rel='self']/@href", ns) {
string("http://localhost:8080/people")
}
}
非同期リクエスト
このセクションでは、MockMvc を単独で使用して非同期リクエスト処理をテストする方法を示します。WebTestClient を介して MockMvc を使用する場合、WebTestClient
はこのセクションで説明されていることを自動的に実行するため、非同期リクエストを機能させるために特別なことは何もしません。
Spring MVC でサポートされている Servlet 3.0 非同期リクエストは、サーブレットコンテナースレッドを終了し、アプリケーションが非同期にレスポンスを計算できるようにすることで機能します。その後、非同期ディスパッチが行われ、サーブレットコンテナースレッドの処理が完了します。
Spring MVC テストでは、最初に生成された非同期値をアサートし、次に手動で非同期ディスパッチを実行し、最後にレスポンスを検証することにより、非同期リクエストをテストできます。以下は、DeferredResult
、Callable
、または Reactor Mono
などのリアクティブ型を返すコントローラーメソッドのテスト例です。
// static import of MockMvcRequestBuilders.* and MockMvcResultMatchers.*
@Test
void test() throws Exception {
MvcResult mvcResult = this.mockMvc.perform(get("/path"))
.andExpect(status().isOk()) (1)
.andExpect(request().asyncStarted()) (2)
.andExpect(request().asyncResult("body")) (3)
.andReturn();
this.mockMvc.perform(asyncDispatch(mvcResult)) (4)
.andExpect(status().isOk()) (5)
.andExpect(content().string("body"));
}
1 | レスポンスステータスを変更しないでください |
2 | 非同期処理が開始されている必要があります |
3 | 待機して非同期結果をアサートする |
4 | ASYNC ディスパッチを手動で実行する (実行中のコンテナーがないため) |
5 | 最終レスポンスを確認する |
@Test
fun test() {
var mvcResult = mockMvc.get("/path").andExpect {
status().isOk() (1)
request { asyncStarted() } (2)
// TODO Remove unused generic parameter
request { asyncResult<Nothing>("body") } (3)
}.andReturn()
mockMvc.perform(asyncDispatch(mvcResult)) (4)
.andExpect {
status().isOk() (5)
content().string("body")
}
}
1 | レスポンスステータスを変更しないでください |
2 | 非同期処理が開始されている必要があります |
3 | 待機して非同期結果をアサートする |
4 | ASYNC ディスパッチを手動で実行する (実行中のコンテナーがないため) |
5 | 最終レスポンスを確認する |
ストリーミングレスポンス
ストリーミングレスポンスのコンテナーレステスト用の SpringMVC テストに組み込まれているオプションはありません。ただし、WebTestClient を介してストリーミングリクエストをテストできます。これは、WebTestClient
を使用して実行中のサーバーをテストできる Spring Boot でもサポートされています。追加の利点の 1 つは、プロジェクト Reactor の StepVerifier
を使用できることです。これにより、データのストリームに対する期待値を宣言できます。
フィルター登録
MockMvc
インスタンスをセットアップするとき、次の例に示すように、1 つ以上のサーブレット Filter
インスタンスを登録できます。
mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build();
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
登録されたフィルターは、spring-test
から MockFilterChain
を介して呼び出され、最後のフィルターは DispatcherServlet
に委譲されます。
MockMvc とエンドツーエンドのテスト
MockMVc は、spring-test
モジュールのサーブレット API モック実装に基づいて構築されており、実行中のコンテナーに依存しません。実際のクライアントとライブサーバーを実行した完全なエンドツーエンドの統合テストと比較すると、いくつかの違いがあります。
これについて考える最も簡単な方法は、空の MockHttpServletRequest
から始めることです。それに追加するものは何でもリクエストはなります。驚くかもしれないのは、デフォルトではコンテキストパスがないことです。jsessionid
Cookie なし ; 転送、エラー、または非同期ディスパッチなし。実際の JSP レンダリングはありません。代わりに、「転送された」および「リダイレクトされた」URL は MockHttpServletResponse
に保存され、期待してアサートできます。
つまり、JSP を使用する場合、リクエストの転送先の JSP ページを検証できますが、HTML はレンダリングされません。つまり、JSP は呼び出されません。ただし、Thymeleaf や Freemarker など、転送に依存しない他のすべてのレンダリングテクノロジーでは、HTML が期待どおりにレスポンス本文にレンダリングされることに注意してください。@ResponseBody
メソッドを介して JSON、XML、その他の形式をレンダリングする場合も同様です。
または、Spring Boot と @SpringBootTest
の完全なエンドツーエンド統合テストのサポートを検討することもできます。Spring Boot リファレンスガイドを参照してください。
それぞれのアプローチには長所と短所があります。Spring MVC テストで提供されるオプションは、従来の単体テストから完全な統合テストまで、スケールの異なるストップです。確かに、Spring MVC テストのオプションはいずれも従来の単体テストのカテゴリに該当しませんが、少し近いものです。例:モックされたサービスをコントローラーに注入することにより、Web レイヤーを分離できます。この場合、実際の Spring 構成を使用して DispatcherServlet
のみを介して Web レイヤーをテストします。また、スタンドアロンセットアップを使用して、一度に 1 つのコントローラーに焦点を合わせ、それを機能させるために必要な構成を手動で提供することもできます。
Spring MVC テストを使用する際のもう 1 つの重要な違いは、概念的には、このようなテストはサーバー側であるため、使用されたハンドラー、HandlerExceptionResolver で例外が処理された場合、モデルのコンテンツ、バインディングエラーを確認できることです。あり、その他の詳細。つまり、サーバーは実際の HTTP クライアントを介してテストするときのように、不透明なボックスではないため、期待を書くのが簡単です。これは通常、クラシックユニットテストの利点です。作成、推論、デバッグは簡単ですが、完全な統合テストの必要性を置き換えるものではありません。同時に、レスポンスがチェックする最も重要なものであるという事実を見失わないようにすることが重要です。つまり、同じプロジェクト内でも、複数のスタイルとテスト戦略を実行する余地があります。
さらなる例
フレームワーク独自のテストには、MockMvc を単独で、または WebTestClient: GitHub (英語) を介して使用する方法を示すことを目的とした多くのサンプル: GitHub (英語) テストが含まれています。さらなるアイデアについては、これらの例を参照してください。
3.7.2. HtmlUnit 統合
Spring は、MockMvc と HtmlUnit (英語) の統合を提供します。これにより、HTML ベースのビューを使用するときに、エンドツーエンドのテストを簡単に実行できます。この統合により、次のことが可能になります。
HtmlUnit (英語) 、WebDriver (英語) 、ゲブ (英語) などのツールを使用して、サーブレットコンテナーにデプロイする必要なく、HTML ページを簡単にテストします。
ページ内で JavaScript をテストします。
必要に応じて、モックサービスを使用してテストし、テストを高速化します。
コンテナー内のエンドツーエンドテストとコンテナー外の統合テスト間でロジックを共有します。
MockMvc は、サーブレットコンテナー(たとえば、Thymeleaf、FreeMarker など)に依存しないテンプレートテクノロジで動作しますが、サーブレットコンテナーに依存するため、JSP では動作しません。 |
HtmlUnit 統合の理由
頭に浮かぶ最も明白な質問は、「なぜこれが必要なのですか?」です。答えは、非常に基本的なサンプルアプリケーションを調べることで見つけることができます。Message
オブジェクトで CRUD 操作をサポートする Spring MVC Web アプリケーションがあるとします。アプリケーションは、すべてのメッセージのページングもサポートしています。どのようにテストしますか?
Spring MVC テストを使用すると、次のように Message
を作成できるかどうかを簡単にテストできます。
MockHttpServletRequestBuilder createMessage = post("/messages/")
.param("summary", "Spring Rocks")
.param("text", "In case you didn't know, Spring Rocks!");
mockMvc.perform(createMessage)
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/messages/123"));
@Test
fun test() {
mockMvc.post("/messages/") {
param("summary", "Spring Rocks")
param("text", "In case you didn't know, Spring Rocks!")
}.andExpect {
status().is3xxRedirection()
redirectedUrl("/messages/123")
}
}
メッセージを作成できるフォームビューをテストする場合はどうでしょうか。例:フォームが次のスニペットのように見えると仮定します。
<form id="messageForm" action="/messages/" method="post">
<div class="pull-right"><a href="/messages/">Messages</a></div>
<label for="summary">Summary</label>
<input type="text" class="required" id="summary" name="summary" value="" />
<label for="text">Message</label>
<textarea id="text" name="text"></textarea>
<div class="form-actions">
<input type="submit" value="Create" />
</div>
</form>
フォームが新しいメッセージを作成するための正しいリクエストを生成することをどのように確認しますか?素朴な試みは次のようになります。
mockMvc.perform(get("/messages/form"))
.andExpect(xpath("//input[@name='summary']").exists())
.andExpect(xpath("//textarea[@name='text']").exists());
mockMvc.get("/messages/form").andExpect {
xpath("//input[@name='summary']") { exists() }
xpath("//textarea[@name='text']") { exists() }
}
このテストにはいくつかの明らかな欠点があります。text
の代わりにパラメーター message
を使用するようにコントローラーを更新すると、HTML フォームがコントローラーと同期していない場合でも、フォームテストに合格し続けます。これを解決するために、次のように 2 つのテストを組み合わせることができます。
String summaryParamName = "summary";
String textParamName = "text";
mockMvc.perform(get("/messages/form"))
.andExpect(xpath("//input[@name='" + summaryParamName + "']").exists())
.andExpect(xpath("//textarea[@name='" + textParamName + "']").exists());
MockHttpServletRequestBuilder createMessage = post("/messages/")
.param(summaryParamName, "Spring Rocks")
.param(textParamName, "In case you didn't know, Spring Rocks!");
mockMvc.perform(createMessage)
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl("/messages/123"));
val summaryParamName = "summary";
val textParamName = "text";
mockMvc.get("/messages/form").andExpect {
xpath("//input[@name='$summaryParamName']") { exists() }
xpath("//textarea[@name='$textParamName']") { exists() }
}
mockMvc.post("/messages/") {
param(summaryParamName, "Spring Rocks")
param(textParamName, "In case you didn't know, Spring Rocks!")
}.andExpect {
status().is3xxRedirection()
redirectedUrl("/messages/123")
}
これにより、テストが不合格になるリスクが軽減されますが、まだいくつかの問題があります。
ページに複数のフォームがある場合はどうなるでしょうか?確かに、XPath 式を更新できますが、より多くの要因を考慮すると、式はより複雑になります。フィールドは正しい型ですか?フィールドは有効になっていますか?等々。
もう 1 つの課題は、予想される作業の 2 倍を行っていることです。最初にビューを検証し、次に検証したばかりの同じパラメーターでビューを送信する必要があります。理想的には、これは一度にすべて実行できます。
最後に、まだいくつかのことを説明できません。例:フォームに JavaScript 検証があり、それもテストしたい場合はどうなるでしょうか?
全体的な問題は、Web ページのテストに単一の対話が含まれないことです。代わりに、ユーザーが Web ページと対話する方法と、その Web ページが他のリソースと対話する方法の組み合わせです。例:フォームビューの結果は、メッセージを作成するためのユーザーへの入力として使用されます。さらに、フォームビューでは、JavaScript 検証など、ページの動作に影響を与える追加のリソースを潜在的に使用できます。
統合テストで解決?
前述の課題を解決するには、エンドツーエンドの統合テストを実行できますが、これにはいくつかの欠点があります。メッセージをページングできるビューのテストを検討してください。次のテストが必要になる場合があります。
メッセージが空の場合、結果が利用できないことを示す通知がページに表示されますか?
ページに単一のメッセージが適切に表示されていますか?
ページはページングを適切にサポートしていますか?
これらのテストを設定するには、データベースに適切なメッセージが含まれていることを確認する必要があります。これは、いくつかの追加の課題につながります。
適切なメッセージがデータベースにあることを確認するのは面倒です。(外部キー制約を考慮してください。)
各テストではデータベースが正しい状態であることを確認する必要があるため、テストが遅くなる可能性があります。
データベースは特定の状態にする必要があるため、テストを並行して実行することはできません。
自動生成された ID、タイムスタンプなどのアイテムでアサーションを実行することは困難です。
これらの課題は、エンドツーエンドの統合テストを完全に放棄する必要があるという意味ではありません。代わりに、詳細テストをリファクタリングして、より速く、より信頼性が高く、副作用のないモックサービスを使用することで、エンドツーエンドの統合テストの数を減らすことができます。その後、単純なワークフローを検証して、すべてが適切に連携することを確認する少数の真のエンドツーエンド統合テストを実装できます。
HtmlUnit 統合を開始
それでは、どのようにしてページの相互作用をテストし、それでもテストスイート内で良好なパフォーマンスを維持することのバランスをとることができますか?答えは、「MockMvc と HtmlUnit を統合することです。」
HtmlUnit 統合オプション
MockMvc と HtmlUnit を統合する場合、いくつかのオプションがあります。
MockMvc および HtmlUnit : 生の HtmlUnit ライブラリを使用する場合は、このオプションを使用します。
MockMvc および WebDriver : このオプションを使用して、統合テストとエンドツーエンドテストの間でコードの開発と再利用を容易にします。
MockMvc および Geb : Groovy をテストに使用し、開発を容易にし、統合テストとエンドツーエンドテストの間でコードを再利用する場合は、このオプションを使用します。
MockMvc および HtmlUnit
このセクションでは、MockMvc と HtmlUnit を統合する方法について説明します。生の HtmlUnit ライブラリを使用する場合は、このオプションを使用します。
MockMvc および HtmlUnit のセットアップ
最初に、net.sourceforge.htmlunit:htmlunit
のテスト依存関係が含まれていることを確認してください。Apache HttpComponents 4.5+ で HtmlUnit を使用するには、HtmlUnit 2.18 以上を使用する必要があります。
次のように、MockMvcWebClientBuilder
を使用して、MockMvc と統合する HtmlUnit WebClient
を簡単に作成できます。
WebClient webClient;
@BeforeEach
void setup(WebApplicationContext context) {
webClient = MockMvcWebClientBuilder
.webAppContextSetup(context)
.build();
}
lateinit var webClient: WebClient
@BeforeEach
fun setup(context: WebApplicationContext) {
webClient = MockMvcWebClientBuilder
.webAppContextSetup(context)
.build()
}
これは、MockMvcWebClientBuilder を使用する簡単な例です。高度な使用箇所については、高度な MockMvcWebClientBuilder を参照してください。 |
これにより、サーバーとして localhost
を参照する URL が、実際の HTTP 接続を必要とせずに MockMvc
インスタンスにリダイレクトされるようになります。他の URL は、通常どおりネットワーク接続を使用してリクエストされます。これにより、CDN の使用を簡単にテストできます。
MockMvc および HtmlUnit の使用箇所
これで、通常どおり HtmlUnit を使用できますが、アプリケーションをサーブレットコンテナーにデプロイする必要はありません。例:次のメッセージを作成するためにビューをリクエストできます:
HtmlPage createMsgFormPage = webClient.getPage("http://localhost/messages/form");
val createMsgFormPage = webClient.getPage("http://localhost/messages/form")
デフォルトのコンテキストパスは "" です。あるいは、高度な MockMvcWebClientBuilder に従って、コンテキストパスを指定できます。 |
HtmlPage
への参照を取得したら、次の例に示すように、フォームに入力して送信し、メッセージを作成できます。
HtmlForm form = createMsgFormPage.getHtmlElementById("messageForm");
HtmlTextInput summaryInput = createMsgFormPage.getHtmlElementById("summary");
summaryInput.setValueAttribute("Spring Rocks");
HtmlTextArea textInput = createMsgFormPage.getHtmlElementById("text");
textInput.setText("In case you didn't know, Spring Rocks!");
HtmlSubmitInput submit = form.getOneHtmlElementByAttribute("input", "type", "submit");
HtmlPage newMessagePage = submit.click();
val form = createMsgFormPage.getHtmlElementById("messageForm")
val summaryInput = createMsgFormPage.getHtmlElementById("summary")
summaryInput.setValueAttribute("Spring Rocks")
val textInput = createMsgFormPage.getHtmlElementById("text")
textInput.setText("In case you didn't know, Spring Rocks!")
val submit = form.getOneHtmlElementByAttribute("input", "type", "submit")
val newMessagePage = submit.click()
最後に、新しいメッセージが正常に作成されたことを確認できます。次のアサーションでは、AssertJ (英語) ライブラリを使用しています。
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123");
String id = newMessagePage.getHtmlElementById("id").getTextContent();
assertThat(id).isEqualTo("123");
String summary = newMessagePage.getHtmlElementById("summary").getTextContent();
assertThat(summary).isEqualTo("Spring Rocks");
String text = newMessagePage.getHtmlElementById("text").getTextContent();
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!");
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123")
val id = newMessagePage.getHtmlElementById("id").getTextContent()
assertThat(id).isEqualTo("123")
val summary = newMessagePage.getHtmlElementById("summary").getTextContent()
assertThat(summary).isEqualTo("Spring Rocks")
val text = newMessagePage.getHtmlElementById("text").getTextContent()
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!")
前述のコードは、さまざまな方法で MockMvc テストを改善しています。まず、フォームを明示的に検証してから、フォームのようなリクエストを作成する必要がなくなりました。代わりに、フォームをリクエストし、記入し、送信することにより、オーバーヘッドを大幅に削減します。
もう 1 つの重要な要素は、HtmlUnit は Mozilla Rhino エンジンを使用する (英語) が JavaScript を評価することです。つまり、ページ内で JavaScript の動作をテストすることもできます。
HtmlUnit の使用に関する追加情報については、HtmlUnit のドキュメント (英語) を参照してください。
高度な MockMvcWebClientBuilder
これまでの例では、Spring TestContext フレームワークによってロードされた WebApplicationContext
に基づいて WebClient
を構築することにより、可能な限り最も簡単な方法で MockMvcWebClientBuilder
を使用しました。このアプローチは、次の例で繰り返されます。
WebClient webClient;
@BeforeEach
void setup(WebApplicationContext context) {
webClient = MockMvcWebClientBuilder
.webAppContextSetup(context)
.build();
}
lateinit var webClient: WebClient
@BeforeEach
fun setup(context: WebApplicationContext) {
webClient = MockMvcWebClientBuilder
.webAppContextSetup(context)
.build()
}
次の例に示すように、追加の構成オプションを指定することもできます。
WebClient webClient;
@BeforeEach
void setup() {
webClient = MockMvcWebClientBuilder
// demonstrates applying a MockMvcConfigurer (Spring Security)
.webAppContextSetup(context, springSecurity())
// for illustration only - defaults to ""
.contextPath("")
// By default MockMvc is used for localhost only;
// the following will use MockMvc for example.com and example.org as well
.useMockMvcForHosts("example.com","example.org")
.build();
}
lateinit var webClient: WebClient
@BeforeEach
fun setup() {
webClient = MockMvcWebClientBuilder
// demonstrates applying a MockMvcConfigurer (Spring Security)
.webAppContextSetup(context, springSecurity())
// for illustration only - defaults to ""
.contextPath("")
// By default MockMvc is used for localhost only;
// the following will use MockMvc for example.com and example.org as well
.useMockMvcForHosts("example.com","example.org")
.build()
}
別の方法として、次のように MockMvc
インスタンスを個別に構成して MockMvcWebClientBuilder
に提供することにより、まったく同じセットアップを実行できます。
MockMvc mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
webClient = MockMvcWebClientBuilder
.mockMvcSetup(mockMvc)
// for illustration only - defaults to ""
.contextPath("")
// By default MockMvc is used for localhost only;
// the following will use MockMvc for example.com and example.org as well
.useMockMvcForHosts("example.com","example.org")
.build();
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
これはより冗長ですが、MockMvc
インスタンスを使用して WebClient
を構築することにより、指先で MockMvc の全機能を使用できます。
MockMvc インスタンスの作成に関する追加情報については、セットアップの選択を参照してください。 |
MockMvc および WebDriver
前のセクションでは、MockMvc を生の HtmlUnit API と組み合わせて使用する方法を見てきました。このセクションでは、Selenium WebDriver (英語) 内で追加の抽象化を使用して、物事をさらに簡単にします。
なぜ WebDriver と MockMvc なのでしょうか?
すでに HtmlUnit と MockMvc を使用できますが、なぜ WebDriver を使用するのでしょうか? Selenium WebDriver は非常にエレガントな API を提供しており、コードを簡単に整理できます。それがどのように機能するかをよりよく示すために、このセクションの例を調べます。
Selenium (英語) の一部であるにもかかわらず、WebDriver はテストを実行するために Selenium Server を必要としません。 |
メッセージが適切に作成されるようにする必要があるとします。テストには、HTML フォームの入力要素の検索、入力、さまざまなアサーションの作成が含まれます。
エラー状態もテストする必要があるため、このアプローチでは多数の個別のテストが行われます。例:フォームの一部のみを入力した場合にエラーが発生するようにします。フォーム全体に入力すると、新しく作成されたメッセージが後で表示されます。
フィールドの 1 つに「summary」という名前が付けられている場合、テスト内の複数の場所で次のようなものが繰り返される可能性があります。
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
summaryInput.setValueAttribute(summary);
val summaryInput = currentPage.getHtmlElementById("summary")
summaryInput.setValueAttribute(summary)
id
を smmry
に変更するとどうなるでしょうか?これを行うと、すべてのテストを更新してこの変更を組み込むように強制されます。これは DRY の原則に違反するため、次のように、理想的にはこのコードを独自のメソッドに抽出する必要があります。
public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {
setSummary(currentPage, summary);
// ...
}
public void setSummary(HtmlPage currentPage, String summary) {
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
summaryInput.setValueAttribute(summary);
}
fun createMessage(currentPage: HtmlPage, summary:String, text:String) :HtmlPage{
setSummary(currentPage, summary);
// ...
}
fun setSummary(currentPage:HtmlPage , summary: String) {
val summaryInput = currentPage.getHtmlElementById("summary")
summaryInput.setValueAttribute(summary)
}
そうすることで、UI を変更した場合にすべてのテストを更新する必要がなくなります。
次の例に示すように、これをさらに一歩進め、現在の HtmlPage
を表す Object
内にこのロジックを配置することもできます。
public class CreateMessagePage {
final HtmlPage currentPage;
final HtmlTextInput summaryInput;
final HtmlSubmitInput submit;
public CreateMessagePage(HtmlPage currentPage) {
this.currentPage = currentPage;
this.summaryInput = currentPage.getHtmlElementById("summary");
this.submit = currentPage.getHtmlElementById("submit");
}
public <T> T createMessage(String summary, String text) throws Exception {
setSummary(summary);
HtmlPage result = submit.click();
boolean error = CreateMessagePage.at(result);
return (T) (error ? new CreateMessagePage(result) : new ViewMessagePage(result));
}
public void setSummary(String summary) throws Exception {
summaryInput.setValueAttribute(summary);
}
public static boolean at(HtmlPage page) {
return "Create Message".equals(page.getTitleText());
}
}
class CreateMessagePage(private val currentPage: HtmlPage) {
val summaryInput: HtmlTextInput = currentPage.getHtmlElementById("summary")
val submit: HtmlSubmitInput = currentPage.getHtmlElementById("submit")
fun <T> createMessage(summary: String, text: String): T {
setSummary(summary)
val result = submit.click()
val error = at(result)
return (if (error) CreateMessagePage(result) else ViewMessagePage(result)) as T
}
fun setSummary(summary: String) {
summaryInput.setValueAttribute(summary)
}
fun at(page: HtmlPage): Boolean {
return "Create Message" == page.getTitleText()
}
}
}
以前は、このパターンはページオブジェクトパターン: GitHub (英語) として知られていました。確かに HtmlUnit でこれを行うことができますが、WebDriver には、このパターンを実装しやすくするために次のセクションで検討するいくつかのツールが用意されています。
MockMvc および WebDriver のセットアップ
Selenium WebDriver を Spring MVC テストフレームワークで使用するには、プロジェクトに org.seleniumhq.selenium:selenium-htmlunit-driver
へのテスト依存関係が含まれていることを確認してください。
次の例に示すように、MockMvcHtmlUnitDriverBuilder
を使用して、MockMvc と統合する Selenium WebDriver を簡単に作成できます。
WebDriver driver;
@BeforeEach
void setup(WebApplicationContext context) {
driver = MockMvcHtmlUnitDriverBuilder
.webAppContextSetup(context)
.build();
}
lateinit var driver: WebDriver
@BeforeEach
fun setup(context: WebApplicationContext) {
driver = MockMvcHtmlUnitDriverBuilder
.webAppContextSetup(context)
.build()
}
これは、MockMvcHtmlUnitDriverBuilder を使用する簡単な例です。より高度な使用箇所については、高度な MockMvcHtmlUnitDriverBuilder を参照してください。 |
上記の例では、サーバーとして localhost
を参照する URL が、実際の HTTP 接続を必要とせずに MockMvc
インスタンスにリダイレクトされるようにします。他の URL は、通常どおりネットワーク接続を使用してリクエストされます。これにより、CDN の使用を簡単にテストできます。
MockMvc および WebDriver の使用箇所
これで、通常どおり WebDriver を使用できますが、アプリケーションをサーブレットコンテナーにデプロイする必要はありません。例:次のメッセージを作成するためにビューをリクエストできます:
CreateMessagePage page = CreateMessagePage.to(driver);
val page = CreateMessagePage.to(driver)
次に、フォームに入力して送信し、次のようにメッセージを作成します。
ViewMessagePage viewMessagePage =
page.createMessage(ViewMessagePage.class, expectedSummary, expectedText);
val viewMessagePage =
page.createMessage(ViewMessagePage::class, expectedSummary, expectedText)
これにより、ページオブジェクトパターンを活用することにより、HtmlUnit テストの設計が改善されます。なぜ WebDriver と MockMvc なのでしょうか? で説明したように、HtmlUnit でページオブジェクトパターンを使用できますが、WebDriver でははるかに簡単です。次の CreateMessagePage
実装を検討してください。
public class CreateMessagePage
extends AbstractPage { (1)
(2)
private WebElement summary;
private WebElement text;
(3)
@FindBy(css = "input[type=submit]")
private WebElement submit;
public CreateMessagePage(WebDriver driver) {
super(driver);
}
public <T> T createMessage(Class<T> resultPage, String summary, String details) {
this.summary.sendKeys(summary);
this.text.sendKeys(details);
this.submit.click();
return PageFactory.initElements(driver, resultPage);
}
public static CreateMessagePage to(WebDriver driver) {
driver.get("http://localhost:9990/mail/messages/form");
return PageFactory.initElements(driver, CreateMessagePage.class);
}
}
1 | CreateMessagePage は AbstractPage を継承します。AbstractPage の詳細については説明しませんが、要約すると、すべてのページに共通の機能が含まれています。例:アプリケーションにナビゲーションバー、グローバルエラーメッセージ、およびその他の機能がある場合、このロジックを共有の場所に配置できます。 |
2 | 関心のある HTML ページの各部分にメンバー変数があります。これらは WebElement タイプです。WebDriver の PageFactory : GitHub (英語) では、各 WebElement を自動的に解決することにより、HtmlUnit バージョンの CreateMessagePage から多くのコードを削除できます。PageFactory#initElements(WebDriver,Class<T>) (英語) メソッドは、フィールド名を使用し、HTML ページ内の要素の id または name で検索することにより、各 WebElement を自動的に解決します。 |
3 | @FindBy アノテーション: GitHub (英語) を使用して、デフォルトのルックアップ動作をオーバーライドできます。この例では、@FindBy アノテーションを使用して、css セレクター(input [type = submit])で送信ボタンを検索する方法を示します。 |
class CreateMessagePage(private val driver: WebDriver) : AbstractPage(driver) { (1)
(2)
private lateinit var summary: WebElement
private lateinit var text: WebElement
(3)
@FindBy(css = "input[type=submit]")
private lateinit var submit: WebElement
fun <T> createMessage(resultPage: Class<T>, summary: String, details: String): T {
this.summary.sendKeys(summary)
text.sendKeys(details)
submit.click()
return PageFactory.initElements(driver, resultPage)
}
companion object {
fun to(driver: WebDriver): CreateMessagePage {
driver.get("http://localhost:9990/mail/messages/form")
return PageFactory.initElements(driver, CreateMessagePage::class.java)
}
}
}
1 | CreateMessagePage は AbstractPage を継承します。AbstractPage の詳細については説明しませんが、要約すると、すべてのページに共通の機能が含まれています。例:アプリケーションにナビゲーションバー、グローバルエラーメッセージ、およびその他の機能がある場合、このロジックを共有の場所に配置できます。 |
2 | 関心のある HTML ページの各部分にメンバー変数があります。これらは WebElement タイプです。WebDriver の PageFactory : GitHub (英語) では、各 WebElement を自動的に解決することにより、HtmlUnit バージョンの CreateMessagePage から多くのコードを削除できます。PageFactory#initElements(WebDriver,Class<T>) (英語) メソッドは、フィールド名を使用し、HTML ページ内の要素の id または name で検索することにより、各 WebElement を自動的に解決します。 |
3 | @FindBy アノテーション: GitHub (英語) を使用して、デフォルトのルックアップ動作をオーバーライドできます。この例では、@FindBy アノテーションを使用して、css セレクター(input [type = submit])で送信ボタンを検索する方法を示します。 |
最後に、新しいメッセージが正常に作成されたことを確認できます。以下のアサーションは、AssertJ (英語) アサーションライブラリを使用します。
assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage);
assertThat(viewMessagePage.getSuccess()).isEqualTo("Successfully created a new message");
assertThat(viewMessagePage.message).isEqualTo(expectedMessage)
assertThat(viewMessagePage.success).isEqualTo("Successfully created a new message")
ViewMessagePage
を使用すると、カスタムドメインモデルとやり取りできることがわかります。例: Message
オブジェクトを返すメソッドを公開します:
public Message getMessage() throws ParseException {
Message message = new Message();
message.setId(getId());
message.setCreated(getCreated());
message.setSummary(getSummary());
message.setText(getText());
return message;
}
fun getMessage() = Message(getId(), getCreated(), getSummary(), getText())
その後、アサーションでリッチドメインオブジェクトを使用できます。
最後に、テストが完了したら、次のように WebDriver
インスタンスを閉じることを忘れないでください。
@AfterEach
void destroy() {
if (driver != null) {
driver.close();
}
}
@AfterEach
fun destroy() {
if (driver != null) {
driver.close()
}
}
WebDriver の使用に関する追加情報については、Selenium WebDriver のドキュメント: GitHub (英語) を参照してください。
高度な MockMvcHtmlUnitDriverBuilder
これまでの例では、Spring TestContext フレームワークによってロードされた WebApplicationContext
に基づいて WebDriver
を構築することにより、可能な限り最も簡単な方法で MockMvcHtmlUnitDriverBuilder
を使用しました。このアプローチは、次のようにここで繰り返されます。
WebDriver driver;
@BeforeEach
void setup(WebApplicationContext context) {
driver = MockMvcHtmlUnitDriverBuilder
.webAppContextSetup(context)
.build();
}
lateinit var driver: WebDriver
@BeforeEach
fun setup(context: WebApplicationContext) {
driver = MockMvcHtmlUnitDriverBuilder
.webAppContextSetup(context)
.build()
}
次のように、追加の構成オプションを指定することもできます。
WebDriver driver;
@BeforeEach
void setup() {
driver = MockMvcHtmlUnitDriverBuilder
// demonstrates applying a MockMvcConfigurer (Spring Security)
.webAppContextSetup(context, springSecurity())
// for illustration only - defaults to ""
.contextPath("")
// By default MockMvc is used for localhost only;
// the following will use MockMvc for example.com and example.org as well
.useMockMvcForHosts("example.com","example.org")
.build();
}
lateinit var driver: WebDriver
@BeforeEach
fun setup() {
driver = MockMvcHtmlUnitDriverBuilder
// demonstrates applying a MockMvcConfigurer (Spring Security)
.webAppContextSetup(context, springSecurity())
// for illustration only - defaults to ""
.contextPath("")
// By default MockMvc is used for localhost only;
// the following will use MockMvc for example.com and example.org as well
.useMockMvcForHosts("example.com","example.org")
.build()
}
別の方法として、次のように MockMvc
インスタンスを個別に構成して MockMvcHtmlUnitDriverBuilder
に提供することにより、まったく同じセットアップを実行できます。
MockMvc mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
driver = MockMvcHtmlUnitDriverBuilder
.mockMvcSetup(mockMvc)
// for illustration only - defaults to ""
.contextPath("")
// By default MockMvc is used for localhost only;
// the following will use MockMvc for example.com and example.org as well
.useMockMvcForHosts("example.com","example.org")
.build();
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed
これはより冗長ですが、MockMvc
インスタンスを使用して WebDriver
を構築することにより、指先で MockMvc の全機能を使用できます。
MockMvc インスタンスの作成に関する追加情報については、セットアップの選択を参照してください。 |
MockMvc および Geb
前のセクションでは、MockMvc を WebDriver と使用する方法を説明しました。このセクションでは、ゲブ (英語) を使用して、テストを Groovy-er にします。
なぜ Geb と MockMvc なのでしょうか?
Geb は WebDriver によってサポートされているため、WebDriver から得られるのと同じ利点の多くを提供します。ただし、Geb は、ボイラープレートコードの一部を処理することで、作業をさらに簡単にします。
MockMvc と Geb のセットアップ
次のように、MockMvc を使用する Selenium WebDriver で Geb Browser
を簡単に初期化できます。
def setup() {
browser.driver = MockMvcHtmlUnitDriverBuilder
.webAppContextSetup(context)
.build()
}
これは、MockMvcHtmlUnitDriverBuilder を使用する簡単な例です。より高度な使用箇所については、高度な MockMvcHtmlUnitDriverBuilder を参照してください。 |
これにより、サーバーとして localhost
を参照する URL が、実際の HTTP 接続を必要とせずに、MockMvc
インスタンスにリダイレクトされるようになります。他の URL は、通常どおりネットワーク接続を使用してリクエストされます。これにより、CDN の使用を簡単にテストできます。
MockMvc と Geb の使用箇所
これで、通常どおり Geb を使用できますが、アプリケーションをサーブレットコンテナーにデプロイする必要はありません。例:次のメッセージを作成するためにビューをリクエストできます:
to CreateMessagePage
次に、フォームに入力して送信し、次のようにメッセージを作成します。
when:
form.summary = expectedSummary
form.text = expectedMessage
submit.click(ViewMessagePage)
認識されないメソッド呼び出し、プロパティアクセス、または見つからない参照は、現在のページオブジェクトに転送されます。これにより、WebDriver を直接使用するときに必要な定型コードの多くが削除されます。
直接 WebDriver を使用する場合と同様に、これはページオブジェクトパターンを使用することにより、HtmlUnit テストの設計を改善します。前述のように、HtmlUnit および WebDriver でページオブジェクトパターンを使用できますが、Geb ではさらに簡単です。Groovy ベースの新しい CreateMessagePage
実装を検討してください。
class CreateMessagePage extends Page {
static url = 'messages/form'
static at = { assert title == 'Messages : Create'; true }
static content = {
submit { $('input[type=submit]') }
form { $('form') }
errors(required:false) { $('label.error, .alert-error')?.text() }
}
}
CreateMessagePage
は Page
を継承します。Page
の詳細については説明しませんが、要約すると、すべてのページに共通の機能が含まれています。このページが見つかる URL を定義します。これにより、次のようにページに移動できます。
to CreateMessagePage
また、指定されたページにいるかどうかを判断する at
クロージャーもあります。正しいページにいる場合は、true
が返されます。これが、次のように正しいページにいると断言できる理由です。
then:
at CreateMessagePage
errors.contains('This field is required.')
クロージャーでアサーションを使用して、間違ったページにいた場合に問題が発生した場所を判断できるようにします。 |
次に、ページ内のすべての関心領域を指定する content
クロージャーを作成します。jQuery 風の Navigator API (英語) を使用して、関心のあるコンテンツを選択できます。
最後に、次のように、新しいメッセージが正常に作成されたことを確認できます。
then:
at ViewMessagePage
success == 'Successfully created a new message'
id
date
summary == expectedSummary
message == expectedMessage
Geb を最大限に活用する方法の詳細については、ゲブの本 (英語) ユーザーマニュアルを参照してください。
3.8. クライアントアプリケーションのテスト
クライアント側のテストを使用して、RestTemplate
を内部的に使用するコードをテストできます。予想されるリクエストを宣言し、「スタブ」レスポンスを提供することにより、コードの分離テスト(サーバーを実行せずに)に集中できるようになります。次の例は、その方法を示しています。
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess());
// Test code that uses the above RestTemplate ...
mockServer.verify();
val restTemplate = RestTemplate()
val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess())
// Test code that uses the above RestTemplate ...
mockServer.verify()
上記の例では、MockRestServiceServer
(クライアント側の REST テストの中心クラス)は、RestTemplate
を、期待に反して実際のリクエストをアサートし、「スタブ」レスポンスを返すカスタム ClientHttpRequestFactory
で構成します。この場合、/greeting
へのリクエストが予想され、text/plain
コンテンツを含む 200 レスポンスを返します。必要に応じて、追加の予想リクエストとスタブレスポンスを定義できます。予想されるリクエストとスタブレスポンスを定義する場合、RestTemplate
は通常どおりクライアント側のコードで使用できます。テストの最後に、mockServer.verify()
を使用して、すべての期待が満たされていることを確認できます。
デフォルトでは、リクエストは予想が宣言された順序で予想されます。サーバーの構築時に ignoreExpectOrder
オプションを設定できます。この場合、すべての期待値が(順番に)チェックされ、特定のリクエストに一致するものが見つかります。つまり、リクエストは任意の順序で送信できます。次の例では、ignoreExpectOrder
を使用しています。
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build()
デフォルトでは、順序付けされていないリクエストでも、各リクエストは一度だけ実行できます。expect
メソッドは、カウント範囲を指定する ExpectedCount
引数を受け入れるオーバーロードされたバリアントを提供します(たとえば、once
、manyTimes
、max
、min
、between
など)。次の例では times
を使用しています。
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(times(2), requestTo("/something")).andRespond(withSuccess());
mockServer.expect(times(3), requestTo("/somewhere")).andRespond(withSuccess());
// ...
mockServer.verify();
val restTemplate = RestTemplate()
val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
mockServer.expect(times(2), requestTo("/something")).andRespond(withSuccess())
mockServer.expect(times(3), requestTo("/somewhere")).andRespond(withSuccess())
// ...
mockServer.verify()
ignoreExpectOrder
が設定されていない(デフォルト)ため、リクエストが宣言の順序で予期される場合、その順序は予期されるリクエストの最初にのみ適用されることに注意してください。たとえば、「/something」が 2 回、「/somewhere」が 3 回と予想される場合、「/somewhere」へのリクエストがある前に「/something」へのリクエストがあるはずですが、その後の「/something」および「/somewhere 「いつでもリクエストを送信できます。
上記のすべての代替として、クライアント側のテストサポートは、RestTemplate
に構成して MockMvc
インスタンスにバインドできる ClientHttpRequestFactory
実装も提供します。これにより、サーバーを実行せずに、実際のサーバー側ロジックを使用してリクエストを処理できます。次の例は、その方法を示しています。
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
this.restTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(mockMvc));
// Test code that uses the above RestTemplate ...
val mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build()
restTemplate = RestTemplate(MockMvcClientHttpRequestFactory(mockMvc))
// Test code that uses the above RestTemplate ...
3.8.1. 静的インポート
サーバー側のテストと同様に、クライアント側のテスト用の流れるような API には、いくつかの静的インポートが必要です。それらは MockRest*
を検索することで簡単に見つけることができます。Eclipse ユーザーは、MockRestRequestMatchers.*
および MockRestResponseCreators.*
を「Java」→「エディター」→「コンテンツアシスト」→「お気に入り」の Eclipse 設定で「お気に入りの静的メンバー」として追加する必要があります。これにより、静的メソッド名の最初の文字を入力した後にコンテンツアシストを使用できます。他の IDE(IntelliJ など)は、追加の構成を必要としない場合があります。静的メンバーでのコード補完のサポートを確認してください。
3.8.2. クライアント側の REST テストのその他の例
Spring MVC Test 独自のテストには、クライアント側の REST テストのテスト例: GitHub (英語) が含まれています。
4. その他のリソース
テストの詳細については、次のリソースを参照してください。
JUnit (英語) : 「プログラマ向けの Java 用テストフレームワーク」。テストスイートで Spring Framework によって使用され、Spring TestContext フレームワークでサポートされています。
TestNG (英語) : テストグループ、データ駆動型テスト、分散テスト、その他の機能のサポートが追加された、JUnit に触発されたテストフレームワーク。Spring TestContext フレームワークでサポート
AssertJ (英語) : Java 8 ラムダ、ストリーム、その他の機能のサポートを含む「Java のフルアサーション」。
モックオブジェクト (英語) : ウィキペディアの記事。
MockObjects.com (英語) : モックオブジェクト専用の Web サイト。テスト駆動開発内のコード設計を改善するための手法です。
Mockito (英語) : Test Spy (英語) パターンに基づく Java モックライブラリ。Spring Framework のテストスイートで使用されます。
EasyMock (英語) : Java ライブラリ「Java のプロキシメカニズムを使用してオンザフライで生成することにより、インターフェース(およびクラス拡張を介したオブジェクト)のモックオブジェクトを提供します。」
JMock (英語) : モックオブジェクトを使用した Java コードのテスト駆動開発をサポートするライブラリ。
DbUnit (英語) : JUnit 拡張機能(Ant および Maven でも使用可能)は、データベース駆動型プロジェクトを対象としており、テスト実行の間にデータベースを既知の状態にします。
テストコンテナー (英語) : JUnit テストをサポートする Java ライブラリ。軽量で使い捨ての一般的なデータベース、Selenium Web ブラウザー、または Docker コンテナーで実行できるその他のインスタンスを提供します。
The Grinder (英語) : Java 負荷テストフレームワーク。
SpringMockK: GitHub (英語) : Mockito の代わりに MockK (英語) を使用して Kotlin で記述された Spring Boot 統合テストのサポート。