此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 spring-cloud-stream 4.1.4! |
测试
Spring Cloud Stream 支持在不连接到消息传递系统的情况下测试微服务应用程序。
Spring 集成测试 Binder
Spring Cloud Stream 带有一个测试 Binder,您可以使用它来测试各种应用程序组件,而无需实际的 Binder 实现或消息代理。
这个测试绑定器充当单元测试和集成测试之间的桥梁,它基于 Spring 集成框架作为 JVM 内部的消息代理,本质上为您提供了两全其美的效果 - 一个没有网络的真正绑定器。
测试 Binder 配置
要启用 Spring 集成测试 Binder,您需要将其添加为依赖项,并使用 .@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")
测试 Binder 使用情况
现在,您可以将微服务作为简单的单元测试来测试。
要启用 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());
}
}
对于您有多个绑定和/或多个输入和输出,或者只是想明确
您要发送到或接收的目标、和 和 的方法被覆盖,以允许您提供输入和输出目标的名称。send()
receive()
InputDestination
OutputDestination
请考虑以下示例:
@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());
}
}
测试 Binder 和 PollableMessageSource
Spring 集成测试 Binder 还允许您在使用时编写测试(有关更多详细信息,请参见 [使用轮询的消费者])。PollableMessageSource
不过,需要理解的重要一点是,轮询不是事件驱动的,而这是一种公开操作以生成(轮询)Message(单数)的策略。
轮询频率、使用的线程数或轮询位置(消息队列或文件系统)完全取决于您;
换句话说,您有责任配置 Poller 或 Threads 或 Message 的实际来源。幸运的是,Spring 有很多抽象来配置这一点。PollableMessageSource
让我们看一下这个例子:
@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 条消息,将它们发送到此 Binder 发送到我们检索它们的位置(对于任何断言)。
目前,它打印以下内容:Source
OutputDestination
Message 1: POLLED DATA
Message 2: POLLED DATA
Message 3: POLLED DATA
如您所见,数据是相同的。这是因为这个 Binder 定义了 actual 的默认实现 - source
从中轮询 Messages using 操作。虽然对于大多数测试方案来说已经足够了,但在某些情况下,您可能希望
以定义您自己的 .为此,只需在测试配置中配置一个 bean 类型,提供您自己的 bean
Message sourcing 的实现。MessageSource
poll()
MessageSource
MessageSource
示例如下:
@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,因为它将与同名(不同类型)的 bean 冲突
由 Spring Boot 提供,原因无关。messageSource |