最新の安定バージョンについては、spring-cloud-stream 5.0.1 を使用してください。 |
テスト
Spring Cloud Stream は、メッセージングシステムに接続せずにマイクロサービスアプリケーションをテストするためのサポートを提供します。
Spring Integration テストバインダー
Spring Cloud Stream には、実際のバインダー実装やメッセージブローカーを必要とせずに、さまざまなアプリケーションコンポーネントをテストするために使用できるテストバインダーが付属しています。
このテストバインダーは、単体テストと統合テスト間のブリッジとして機能し、JVM 内メッセージブローカーとして Spring Integration フレームワークに基づいており、本質的に両方の長所を提供します。つまり、ネットワークのない実際のバインダーです。
バインダー構成のテスト
Spring Integration テストバインダーを有効にするには、それを依存関係として追加し、クラスに @EnableTestBinder のアノテーションを付ける必要があります。
必須依存関係を追加
以下は、必要な Maven POM エントリの例です。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-test-binder</artifactId>
<scope>test</scope>
</dependency>または build.gradle.kts の場合
testImplementation("org.springframework.cloud:spring-cloud-stream-test-binder")バインダーの使用箇所をテストする
これで、マイクロサービスを単純な単体テストとしてテストできます。テストバインダーを有効にするには、クラスに @EnableTestBinder アノテーションを付けます。
@SpringBootTest
public class SampleStreamTests {
@Autowired
private InputDestination input;
@Autowired
private OutputDestination output;
@Test
public void testEmptyConfiguration() {
this.input.send(new GenericMessage<byte[]>("hello".getBytes()));
assertThat(output.receive().getPayload()).isEqualTo("HELLO".getBytes());
}
@SpringBootApplication
@EnableTestBinder
public static class SampleConfiguration {
@Bean
public Function<String, String> uppercase() {
return v -> v.toUpperCase();
}
}
}また、より詳細な制御が必要な場合、同じテストスイートで複数の構成をテストする場合は、次の操作も実行できます。
@EnableAutoConfiguration
public static class MyTestConfiguration {
@Bean
public Function<String, String> uppercase() {
return v -> v.toUpperCase();
}
}
. . .
@Test
public void sampleTest() {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
TestChannelBinderConfiguration.getCompleteConfiguration(
MyTestConfiguration.class))
.run("--spring.cloud.function.definition=uppercase")) {
InputDestination source = context.getBean(InputDestination.class);
OutputDestination target = context.getBean(OutputDestination.class);
source.send(new GenericMessage<byte[]>("hello".getBytes()));
assertThat(target.receive().getPayload()).isEqualTo("HELLO".getBytes());
}
} 複数のバインディングや複数の入力と出力がある場合、または単に送受信する宛先の名前を明示したい場合は、InputDestination と OutputDestination の send() メソッドと receive() メソッドをオーバーライドして、提供できるようにします。入力および出力先の名前。
次のサンプルについて考えてみます。
@EnableAutoConfiguration
public static class SampleFunctionConfiguration {
@Bean
public Function<String, String> uppercase() {
return value -> value.toUpperCase();
}
@Bean
public Function<String, String> reverse() {
return value -> new StringBuilder(value).reverse().toString();
}
}そして実際のテスト
@Test
public void testMultipleFunctions() {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
TestChannelBinderConfiguration.getCompleteConfiguration(
SampleFunctionConfiguration.class))
.run("--spring.cloud.function.definition=uppercase;reverse")) {
InputDestination inputDestination = context.getBean(InputDestination.class);
OutputDestination outputDestination = context.getBean(OutputDestination.class);
Message<byte[]> inputMessage = MessageBuilder.withPayload("Hello".getBytes()).build();
inputDestination.send(inputMessage, "uppercase-in-0");
inputDestination.send(inputMessage, "reverse-in-0");
Message<byte[]> outputMessage = outputDestination.receive(0, "uppercase-out-0");
assertThat(outputMessage.getPayload()).isEqualTo("HELLO".getBytes());
outputMessage = outputDestination.receive(0, "reverse-out-0");
assertThat(outputMessage.getPayload()).isEqualTo("olleH".getBytes());
}
}destination などの追加のマッピングプロパティがある場合は、それらの名前を使用する必要があります。例: uppercase 関数の入力と出力を myInput および myOutput バインディング名に明示的にマップする前述のテストの別のバージョンを検討してください。
@Test
public void testMultipleFunctions() {
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
TestChannelBinderConfiguration.getCompleteConfiguration(
SampleFunctionConfiguration.class))
.run(
"--spring.cloud.function.definition=uppercase;reverse",
"--spring.cloud.stream.bindings.uppercase-in-0.destination=myInput",
"--spring.cloud.stream.bindings.uppercase-out-0.destination=myOutput"
)) {
InputDestination inputDestination = context.getBean(InputDestination.class);
OutputDestination outputDestination = context.getBean(OutputDestination.class);
Message<byte[]> inputMessage = MessageBuilder.withPayload("Hello".getBytes()).build();
inputDestination.send(inputMessage, "myInput");
inputDestination.send(inputMessage, "reverse-in-0");
Message<byte[]> outputMessage = outputDestination.receive(0, "myOutput");
assertThat(outputMessage.getPayload()).isEqualTo("HELLO".getBytes());
outputMessage = outputDestination.receive(0, "reverse-out-0");
assertThat(outputMessage.getPayload()).isEqualTo("olleH".getBytes());
}
}テストバインダーと PollableMessageSource
Spring Integration Test Binder を使用すると、PollableMessageSource を使用するときにテストを作成することもできます(詳細については、[ ポーリングされたコンシューマーの使用 ] を参照してください)。
ただし、理解する必要がある重要なことは、ポーリングはイベント駆動型ではなく、PollableMessageSource は、メッセージ(単数)を生成(ポーリング)する操作を公開する戦略であるということです。ポーリングする頻度、使用するスレッドの数、ポーリング元(メッセージキューまたはファイルシステム)は完全にあなた次第です。言い換えると、ポーラーやスレッド、または実際のメッセージのソースを構成するのはユーザーの責任です。幸いなことに、Spring には、それを正確に構成するための抽象化がたくさんあります。
例を見てみましょう:
@Test
public void samplePollingTest() {
ApplicationContext context = new SpringApplicationBuilder(SamplePolledConfiguration.class)
.web(WebApplicationType.NONE)
.run("--spring.jmx.enabled=false", "--spring.cloud.stream.pollable-source=myDestination");
OutputDestination destination = context.getBean(OutputDestination.class);
System.out.println("Message 1: " + new String(destination.receive().getPayload()));
System.out.println("Message 2: " + new String(destination.receive().getPayload()));
System.out.println("Message 3: " + new String(destination.receive().getPayload()));
}
@EnableTestBinder
@EnableAutoConfiguration
public static class SamplePolledConfiguration {
@Bean
public ApplicationRunner poller(PollableMessageSource polledMessageSource, StreamBridge output, TaskExecutor taskScheduler) {
return args -> {
taskScheduler.execute(() -> {
for (int i = 0; i < 3; i++) {
try {
if (!polledMessageSource.poll(m -> {
String newPayload = ((String) m.getPayload()).toUpperCase();
output.send("myOutput", newPayload);
})) {
Thread.sleep(2000);
}
}
catch (Exception e) {
// handle failure
}
}
});
};
}
} 上記の (非常に初歩的な) 例は、2 秒間隔で 3 つのメッセージを生成し、Source の出力先に送信します。このメッセージは、このバインダーが OutputDestination に送信し、そこで取得します (アサーションの場合)。現在、次のように出力されます。
Message 1: POLLED DATA
Message 2: POLLED DATA
Message 3: POLLED DATA ご覧のとおり、データは同じです。これは、このバインダーが実際の MessageSource のデフォルトの実装(poll() 操作を使用してメッセージがポーリングされるソース)を定義しているためです。ほとんどのテストシナリオには十分ですが、独自の MessageSource を定義したい場合があります。これを行うには、テスト構成で型 MessageSource の Bean を構成し、メッセージソーシングの独自の実装を提供します。
次に例を示します。
@Bean
public MessageSource<?> source() {
return () -> new GenericMessage<>("My Own Data " + UUID.randomUUID());
}次の出力をレンダリングします。
Message 1: MY OWN DATA 1C180A91-E79F-494F-ABF4-BA3F993710DA
Message 2: MY OWN DATA D8F3A477-5547-41B4-9434-E69DA7616FEE
Message 3: MY OWN DATA 20BF2E64-7FF4-4CB6-A823-4053D30B5C74 この Bean messageSource には、無関係な理由で Spring Boot によって提供される同じ名前(異なる型)の Bean と競合するため、名前を付けないでください。 |