テストサポート
Spring Integration は、アプリケーションのテストに役立つ多くのユーティリティとアノテーションを提供します。テストのサポートは、2 つのモジュールによって提供されます。
spring-integration-test-support
にはコアアイテムと共有ユーティリティが含まれていますspring-integration-test
は、統合テスト用のモックおよびアプリケーションコンテキスト構成コンポーネントを提供します
spring-integration-test-support
(5.0 より前のバージョンでは spring-integration-test
)は、単体テスト用の基本的なスタンドアロンユーティリティ、ルール、マッチャーを提供します。(Spring Integration 自体にも依存せず、フレームワークテストで内部的に使用されます)。spring-integration-test
は、統合テストを支援することを目的としており、統合コンポーネント全体をモックし、統合フロー全体またはその一部のみを含む個々のコンポーネントの動作を検証する包括的な高レベル API を提供します。
企業でのテストの徹底的な取り扱いは、このリファレンスマニュアルの範囲を超えています。ターゲット統合ソリューションをテストするためのアイデアと原則のソースについては、Gregor Hohpe と Wendy Istvanick による “エンタープライズ統合プロジェクトでのテスト駆動開発” (英語) ペーパーを参照してください。
Spring Integration テストフレームワークおよびテストユーティリティは、既存の JUnit、Hamcrest、Mockito ライブラリに完全に基づいています。アプリケーションコンテキストの相互作用は、Spring Test フレームワークに基づいています。詳細については、それらのプロジェクトのドキュメントを参照してください。
Spring Integration フレームワークおよびその第一級オブジェクト(MessageChannel
、Endpoint
、MessageHandler
など)での EIP の標準的な実装、抽象化、疎結合の原則のおかげで、あらゆる複雑さの統合ソリューションを実装できます。フロー定義用の Spring Integration API を使用すると、統合ソリューションの他のコンポーネントに(ほとんど)影響を与えることなく、フローの一部を改善、変更、置き換えることができます。このような統合ソリューションのテストは、エンドツーエンドのアプローチと分離アプローチの両方から、依然として課題です。いくつかの既存のツールは、いくつかの統合プロトコルをテストまたはモックするのに役立ち、Spring Integration チャネルアダプターでうまく機能します。このようなツールの例には、次のものがあります。
Spring
MockMVC
およびそのMockRestServiceServer
は、HTTP のテストに使用できます。一部の RDBMS ベンダーは、JDBC または JPA サポート用の埋め込みデータベースを提供しています。
ActiveMQ は、JMS または STOMP プロトコルのテスト用に埋め込むことができます。
組み込み MongoDB および Redis 用のツールがあります。
Tomcat および Jetty には、実際の HTTP、Web サービス、WebSockets をテストするためのライブラリが組み込まれています。
Apache Mina プロジェクトの
FtpServer
とSshServer
は、FTP および SFTP プロトコルのテストに使用できます。Hazelcast は、テストで実際のデータグリッドノードとして実行できます。
キュレーターフレームワークは、Zookeeper インタラクション用の
TestingServer
を提供します。Apache Kafka は、テストに Kafka ブローカーを埋め込むための管理ツールを提供します。
GreenMail は、テスト目的のオープンソースで直感的で使いやすいメールサーバーのテストスイートです。
これらのツールとライブラリのほとんどは、Spring Integration テストで使用されます。また、GitHub リポジトリ [GitHub] (英語) (各モジュールの test
ディレクトリ内)から、統合ソリューション用の独自のテストを作成する方法のアイデアを見つけることができます。
この章の残りの部分では、Spring Integration が提供するテストツールとユーティリティについて説明します。
テストユーティリティ
spring-integration-test-support
モジュールは、ユニットテスト用のユーティリティとヘルパーを提供します。
TestUtils
次の例に示すように、TestUtils
クラスは主に JUnit テストのプロパティアサーションに使用されます。
@Test
public void loadBalancerRef() {
MessageChannel channel = channels.get("lbRefChannel");
LoadBalancingStrategy lbStrategy = TestUtils.getPropertyValue(channel,
"dispatcher.loadBalancingStrategy", LoadBalancingStrategy.class);
assertTrue(lbStrategy instanceof SampleLoadBalancingStrategy);
}
TestUtils.getPropertyValue()
は Spring の DirectFieldAccessor
に基づいており、ターゲットのプライベートプロパティから値を取得する機能を提供します。前の例に示すように、ドット表記を使用したネストされたプロパティアクセスもサポートしています。
createTestApplicationContext()
ファクトリメソッドは、指定された Spring Integration 環境で TestApplicationContext
インスタンスを生成します。
このクラスの詳細については、他の TestUtils
メソッドの Javadoc を参照してください。
OnlyOnceTrigger
を使用する
OnlyOnceTrigger
(Javadoc) は、1 つのテストメッセージのみを生成し、他の期間のメッセージに影響を与えずに動作を検証する必要がある場合に、エンドポイントのポーリングに役立ちます。次の例は、OnlyOnceTrigger
を構成する方法を示しています。
<bean id="testTrigger" class="org.springframework.integration.test.util.OnlyOnceTrigger" />
<int:poller id="jpaPoller" trigger="testTrigger">
<int:transactional transaction-manager="transactionManager" />
</int:poller>
次の例は、前述の OnlyOnceTrigger
の構成をテストに使用する方法を示しています。
@Autowired
@Qualifier("jpaPoller")
PollerMetadata poller;
@Autowired
OnlyOnceTrigger testTrigger;
@Test
@DirtiesContext
public void testWithEntityClass() throws Exception {
this.testTrigger.reset();
...
JpaPollingChannelAdapter jpaPollingChannelAdapter = new JpaPollingChannelAdapter(jpaExecutor);
SourcePollingChannelAdapter adapter = JpaTestUtils.getSourcePollingChannelAdapter(
jpaPollingChannelAdapter, this.outputChannel, this.poller, this.context,
this.getClass().getClassLoader());
adapter.start();
...
}
JUnit のルールと条件
LongRunningIntegrationTest
JUnit 4 テストルールは、RUN_LONG_INTEGRATION_TESTS
環境またはシステムプロパティが true
に設定されている場合にテストを実行する必要があるかどうかを示すために存在します。それ以外の場合はスキップされます。バージョン 5.1 以降と同じ理由で、JUnit 5 テスト用に @LongRunningTest
条件付きアノテーションが提供されています。
Hamcrest と Mockito マッチャー
org.springframework.integration.test.matcher
パッケージには、ユニットテストで Message
とそのプロパティをアサートするためのいくつかの Matcher
実装が含まれています。次の例は、そのようなマッチャー(PayloadMatcher
)の使用方法を示しています。
import static org.springframework.integration.test.matcher.PayloadMatcher.hasPayload;
...
@Test
public void transform_withFilePayload_convertedToByteArray() throws Exception {
Message<?> result = this.transformer.transform(message);
assertThat(result, is(notNullValue()));
assertThat(result, hasPayload(is(instanceOf(byte[].class))));
assertThat(result, hasPayload(SAMPLE_CONTENT.getBytes(DEFAULT_ENCODING)));
}
MockitoMessageMatchers
ファクトリは、次の例に示すように、スタブと検証のモックに使用できます。
static final Date SOME_PAYLOAD = new Date();
static final String SOME_HEADER_VALUE = "bar";
static final String SOME_HEADER_KEY = "test.foo";
...
Message<?> message = MessageBuilder.withPayload(SOME_PAYLOAD)
.setHeader(SOME_HEADER_KEY, SOME_HEADER_VALUE)
.build();
MessageHandler handler = mock(MessageHandler.class);
handler.handleMessage(message);
verify(handler).handleMessage(messageWithPayload(SOME_PAYLOAD));
verify(handler).handleMessage(messageWithPayload(is(instanceOf(Date.class))));
...
MessageChannel channel = mock(MessageChannel.class);
when(channel.send(messageWithHeaderEntry(SOME_HEADER_KEY, is(instanceOf(Short.class)))))
.thenReturn(true);
assertThat(channel.send(message), is(false));
Spring Integration とテストコンテキスト
通常、Spring アプリケーションのテストでは、Spring Test フレームワークを使用します。Spring Integration は Spring Framework 基盤に基づいているため、Spring Test フレームワークで実行できるすべてのことは、統合フローをテストするときにも適用されます。org.springframework.integration.test.context
パッケージは、統合のニーズに合わせてテストコンテキストを拡張するためのコンポーネントをいくつか提供します。最初に、次の例に示すように、Spring Integration テストフレームワークを有効にするために、@SpringIntegrationTest
アノテーションを使用してテストクラスを構成します。
@SpringJUnitConfig
@SpringIntegrationTest(noAutoStartup = {"inboundChannelAdapter", "*Source*"})
public class MyIntegrationTests {
@Autowired
private MockIntegrationContext mockIntegrationContext;
}
@SpringIntegrationTest
アノテーションは MockIntegrationContext
Bean にデータを入力します。これは、テストクラスにオートワイヤーしてそのメソッドにアクセスできます。noAutoStartup
オプションを使用すると、Spring Integration テストフレームワークは、通常 autoStartup=true
であるエンドポイントが開始するのを防ぎます。エンドポイントは、提供されたパターンに一致します。これは、次の単純なパターンスタイルをサポートします: xxx*
、xxx
、*xxx
、xxx*yyy
。
これは、受信チャネルアダプター(たとえば、AMQP 受信ゲートウェイ、JDBC ポーリングチャネルアダプター、クライアントモードの WebSocket メッセージプロデューサーなど)からターゲットシステムに実際に接続したくない場合に役立ちます。
@SpringIntegrationTest
は org.springframework.test.context.NestedTestConfiguration
セマンティクスを尊重するため、外部クラス (またはそのスーパークラス) で宣言できます。また、@SpringIntegrationTest
環境は継承された @Nested
テストで使用できます。
MockIntegrationContext
は、実際のアプリケーションコンテキストで Bean を変更するためのターゲットテストケースで使用することを目的としています。例: autoStartup
が false
にオーバーライドされているエンドポイントは、次の例に示すように、モックに置き換えることができます。
@Test
public void testMockMessageSource() {
MessageSource<String> messageSource = () -> new GenericMessage<>("foo");
this.mockIntegrationContext.substituteMessageSourceFor("mySourceEndpoint", messageSource);
Message<?> receive = this.results.receive(10_000);
assertNotNull(receive);
}
ここでの mySourceEndpoint は、実際の MessageSource をモックに置き換える SourcePollingChannelAdapter の Bean 名を指します。同様に、MockIntegrationContext.substituteMessageHandlerFor() は、エンドポイントとして MessageHandler をラップする IntegrationConsumer の Bean 名を予期します。 |
テストの実行後、MockIntegrationContext.resetBeans()
を使用してエンドポイント Bean の状態を実際の構成に復元できます。
@After
public void tearDown() {
this.mockIntegrationContext.resetBeans();
}
バージョン 6.3 以降、MockIntegrationContext.substituteTriggerFor()
API が導入されました。これを使用して、AbstractPollingEndpoint
内の実際の Trigger
を置き換えることができます。たとえば、本番構成は毎日 (または毎週) の cron スケジュールに依存する場合があります。カスタム Trigger
をターゲットエンドポイントに挿入して、期間を軽減できます。例: 上記の OnlyOnceTrigger
は、ポーリングタスクをすぐにスケジュールし、それを 1 回だけ実行する動作を提案します。
詳細については、Javadoc を参照してください。
統合モック
org.springframework.integration.test.mock
パッケージは、Spring Integration コンポーネントのアクティビティのモック、スタブ、検証のためのツールとユーティリティを提供します。モッキング機能は、よく知られている Mockito フレームワークに完全に基づいており、互換性があります。(現在の Mockito 推移的依存関係は、バージョン 2.5.x 以降です。)
MockIntegration
MockIntegration
ファクトリは、統合フロー(MessageSource
、MessageProducer
、MessageHandler
、MessageChannel
)の一部である Spring Integration Bean のモックを構築するための API を提供します。次の例に示すように、構成フェーズ中およびターゲットテストメソッドでターゲットモックを使用して、検証とアサーションを実行する前に実際のエンドポイントを置き換えることができます。
<int:inbound-channel-adapter id="inboundChannelAdapter" channel="results">
<bean class="org.springframework.integration.test.mock.MockIntegration" factory-method="mockMessageSource">
<constructor-arg value="a"/>
<constructor-arg>
<array>
<value>b</value>
<value>c</value>
</array>
</constructor-arg>
</bean>
</int:inbound-channel-adapter>
次の例は、Java 構成を使用して前述の例と同じ構成を実現する方法を示しています。
@InboundChannelAdapter(channel = "results")
@Bean
public MessageSource<Integer> testingMessageSource() {
return MockIntegration.mockMessageSource(1, 2, 3);
}
...
StandardIntegrationFlow flow = IntegrationFlow
.from(MockIntegration.mockMessageSource("foo", "bar", "baz"))
.<String, String>transform(String::toUpperCase)
.channel(out)
.get();
IntegrationFlowRegistration registration = this.integrationFlowContext.registration(flow)
.register();
この目的のために、次の例が示すように、前述の MockIntegrationContext
をテストから使用する必要があります。
this.mockIntegrationContext.substituteMessageSourceFor("mySourceEndpoint",
MockIntegration.mockMessageSource("foo", "bar", "baz"));
Message<?> receive = this.results.receive(10_000);
assertNotNull(receive);
assertEquals("FOO", receive.getPayload());
Mockito MessageSource
モックオブジェクトとは異なり、MockMessageHandler
は、受信メッセージのスタブ処理に対するチェーン API を備えた通常の AbstractMessageProducingHandler
拡張機能です。MockMessageHandler
は、次のリクエストメッセージに一方向スタブを指定するための handleNext(Consumer<Message<?>>)
を提供します。これは、応答を生成しないメッセージハンドラーをモックするために使用されます。handleNextAndReply(Function<Message<?>, ?>)
は、次のリクエストメッセージに対して同じスタブロジックを実行し、それに対する応答を生成するために提供されています。連鎖させて、予想されるすべてのリクエストメッセージバリアントの任意のリクエスト / 応答シナリオをシミュレートできます。これらのコンシューマーと関数は、スタックから最後まで一度に 1 つずつ受信メッセージに適用され、最後のメッセージは残りのすべてのメッセージに使用されます。動作は、Mockito Answer
または doReturn()
API に似ています。
さらに、コンストラクター引数で MockMessageHandler
に Mockito ArgumentCaptor<Message<?>>
を指定できます。MockMessageHandler
の各リクエストメッセージは、その ArgumentCaptor
によってキャプチャーされます。テスト中に、getValue()
および getAllValues()
メソッドを使用して、これらのリクエストメッセージを検証およびアサートできます。
MockIntegrationContext
は substituteMessageHandlerFor()
API を提供します。これにより、テスト中のエンドポイントで実際に構成された MessageHandler
を MockMessageHandler
に置き換えることができます。
次の例は、一般的な使用シナリオを示しています。
ArgumentCaptor<Message<?>> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
MessageHandler mockMessageHandler =
mockMessageHandler(messageArgumentCaptor)
.handleNextAndReply(m -> m.getPayload().toString().toUpperCase());
this.mockIntegrationContext.substituteMessageHandlerFor("myService.serviceActivator",
mockMessageHandler);
GenericMessage<String> message = new GenericMessage<>("foo");
this.myChannel.send(message);
Message<?> received = this.results.receive(10000);
assertNotNull(received);
assertEquals("FOO", received.getPayload());
assertSame(message, messageArgumentCaptor.getValue());
通常の MessageHandler モック (または MockMessageHandler ) は、ReactiveMessageHandler 構成の ReactiveStreamsConsumer に対しても使用する必要があります。 |
詳細については、MockIntegration
(Javadoc) および MockMessageHandler
(Javadoc) Javadoc を参照してください。
その他のリソース
フレームワーク自体のテストケースを調べるだけでなく、Spring Integration サンプルリポジトリ [GitHub] (英語) には、testing-examples
や advanced-testing-examples
など、テストを表示するために特別に作成されたサンプルアプリケーションがいくつかあります。場合によっては、サンプル自体に file-split-ftp
サンプルなどの包括的なエンドツーエンドテストがあります。