此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Integration 6.5.1! |
测试支持
Spring Integration 提供了许多实用程序和注释来帮助您测试应用程序。 测试支持由两个模块提供:
-
spring-integration-test-support
包含核心项目和共享实用程序 -
spring-integration-test
为集成测试提供模拟和应用程序上下文配置组件
spring-integration-test-support
(spring-integration-test
在 5.0 之前的版本中)为单元测试提供了基本的、独立的实用程序、规则和匹配器。
(它也不依赖于 Spring Integration 本身,并在框架测试中内部使用)。spring-integration-test
旨在帮助进行集成测试,并提供全面的高级 API 来模拟集成组件并验证单个组件的行为,包括整个集成流或仅部分组件。
对企业测试的彻底处理超出了本参考手册的范围。 参见 Gregor Hohpe 和 Wendy Istvanick 撰写的 “企业集成项目中的测试驱动开发” 论文,了解测试目标集成解决方案的想法和原则。
Spring Integration 测试框架和测试实用程序完全基于现有的 JUnit、Hamcrest 和 Mockito 库。 应用程序上下文交互基于 Spring 测试框架。 有关详细信息,请参阅这些项目的文档。
得益于 Spring Integration Framework 中 EIP 的规范实现及其一等公民(例如MessageChannel
,Endpoint
和MessageHandler
)、抽象和松耦合原则,您可以实现任何复杂程度的集成解决方案。
使用用于流定义的 Spring Integration API,您可以改进、修改甚至替换流的某些部分,而不会影响集成解决方案中的(大部分)其他组件。
测试这样的集成解决方案仍然是一个挑战,无论是从端到端方法还是从隔离方法来看。
一些现有的工具可以帮助测试或模拟某些集成协议,并且它们与 Spring Integration 通道适配器配合得很好。
此类工具的示例包括:
-
Spring
MockMVC
及其MockRestServiceServer
可用于测试 HTTP。 -
一些 RDBMS 提供商为 JDBC 或 JPA 支持提供嵌入式数据库。
-
可以嵌入 ActiveMQ 以测试 JMS 或 STOMP 协议。
-
有用于嵌入式 MongoDB 和 Redis 的工具。
-
Tomcat 和 Jetty 具有嵌入式库来测试真正的 HTTP、Web 服务或 WebSocket。
-
这
FtpServer
和SshServer
来自 Apache Mina 项目的 FTP 和 SFTP 协议可用于测试 FTP 和 SFTP 协议。 -
Hazelcast 可以在测试中作为真实数据网格节点运行。
-
策展人框架提供了一个
TestingServer
用于 Zookeeper 交互。 -
Apache Kafka 提供了用于在测试中嵌入 Kafka 代理的管理工具。
-
GreenMail 是一个开源、直观且易于使用的电子邮件服务器测试套件,用于测试目的。
这些工具和库中的大多数都用于 Spring Integration 测试。
此外,从 GitHub 存储库(在test
每个模块的目录),您可以发现有关如何为集成解决方案构建自己的测试的想法。
本章的其余部分描述了 Spring Integration 提供的测试工具和实用程序。
测试实用程序
这spring-integration-test-support
模块提供了用于单元测试的实用程序和帮助程序。
测试Utilities
这TestUtils
class 主要用于 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()
factory 方法会产生一个TestApplicationContext
实例与提供的 Spring Integration 环境。
请参阅其他 JavadocTestUtils
方法,了解有关此类的更多信息。
用OnlyOnceTrigger
OnlyOnceTrigger
当只需要生成一条测试消息并验证行为而不影响其他周期消息时,对于轮询端点非常有用。
以下示例演示如何配置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 条件
这@LongRunningTest
存在条件注释,以指示在RUN_LONG_INTEGRATION_TESTS
environment 或 system 属性设置为true
.
否则,将跳过它。
Hamcrest 和 Mockito 匹配器
这org.springframework.integration.test.matcher
包包含多个Matcher
要断言的实现Message
及其在单元测试中的属性。
以下示例演示如何使用一个这样的匹配器 (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
factory 可用于存根和验证的模拟,如以下示例所示:
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 集成和测试上下文
通常,Spring 应用程序的测试使用 Spring 测试框架。
由于 Spring Integration 基于 Spring Framework 基础,因此我们可以使用 Spring Test Framework 执行的所有作也适用于测试集成流。
这org.springframework.integration.test.context
package 提供了一些组件来增强测试上下文以满足集成需求。
首先,我们使用@SpringIntegrationTest
注释以启用 Spring Integration 测试框架,如以下示例所示:
@SpringJUnitConfig
@SpringIntegrationTest(noAutoStartup = {"inboundChannelAdapter", "*Source*"})
public class MyIntegrationTests {
@Autowired
private MockIntegrationContext mockIntegrationContext;
}
这@SpringIntegrationTest
注释填充MockIntegrationContext
bean,您可以将其自动连接到测试类以访问其方法。
使用noAutoStartup
选项,Spring Integration Test Framework 会阻止通常autoStartup=true
从开始。
端点与提供的模式匹配,这些模式支持以下简单模式样式:xxx*
,xxx
,*xxx
和xxx*yyy
.
当我们不希望从入站通道适配器(例如,AMQP 入站网关、JDBC 轮询通道适配器、客户端模式下的 WebSocket 消息生产器等)与目标系统建立实际连接时,这非常有用。
这@SpringIntegrationTest
尊重org.springframework.test.context.NestedTestConfiguration
语义,因此它可以在外部类(甚至是它的超类)上声明 - 和@SpringIntegrationTest
环境将可用于继承@Nested
测试。
这MockIntegrationContext
旨在用于目标测试用例,以便在实际应用程序上下文中对 Bean 进行修改。
例如,具有autoStartup
overrideden 为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 这里指的是SourcePollingChannelAdapter 为此,我们将实数替换为MessageSource 与我们的模拟。
同样,MockIntegrationContext.substituteMessageHandlerFor() 需要一个 bean 名称IntegrationConsumer ,它包装了一个MessageHandler 作为端点。 |
执行测试后,您可以使用以下命令将端点 Bean 的状态恢复到实际配置MockIntegrationContext.resetBeans()
:
@AfterEach
public void tearDown() {
this.mockIntegrationContext.resetBeans();
}
从 6.3 版开始,MockIntegrationContext.substituteTriggerFor()
引入了 API。
这可以用来代替真实的Trigger
在AbstractPollingEndpoint
.
例如,生产配置可能依赖于每日(甚至每周)的 cron 计划。
任何自定义Trigger
可以注入目标端点以缓解时间跨度。
例如,上面提到的OnlyOnceTrigger
建议立即安排轮询任务并仅执行一次的行为。
有关更多信息,请参阅 Javadoc。
集成模拟
这org.springframework.integration.test.mock
package 提供了用于模拟、存根和验证 Spring Integration 组件上的活动的工具和实用程序。
模拟功能完全基于著名的 Mockito 框架并与之兼容。
(当前的 Mockito 传递依赖项是 2.5.x 或更高版本。
模拟集成
这MockIntegration
factory 提供了一个 API 来为 Spring Integration Bean 构建模拟,这些 Bean 是集成流的一部分(MessageSource
,MessageProducer
,MessageHandler
和MessageChannel
).
您可以在配置阶段以及目标测试方法中使用目标模拟来替换实际端点,然后再执行验证和断言,如以下示例所示:
<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
mock 对象,则MockMessageHandler
是常客AbstractMessageProducingHandler
扩展,以链 API 来处理传入消息的存根。
这MockMessageHandler
提供handleNext(Consumer<Message<?>>)
为下一个请求消息指定单向存根。
它用于模拟不产生回复的消息处理程序。
这handleNextAndReply(Function<Message<?>, ?>)
用于对下一个请求消息执行相同的存根逻辑并为其生成回复。
可以将它们链接起来,以模拟所有预期请求消息变体的任何任意请求-回复方案。
这些使用者和函数将应用于传入消息,从堆栈中一次一个,直到最后一条,然后用于所有剩余消息。
行为类似于 MockitoAnswer
或doReturn()
应用程序接口。
此外,您还可以提供 MockitoArgumentCaptor<Message<?>>
到MockMessageHandler
在构造函数参数中。
每个请求消息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 mocking(或MockMessageHandler ) 甚至必须用于ReactiveStreamsConsumer 使用ReactiveMessageHandler 配置。 |
请参阅MockIntegration
和MockMessageHandler
Javadoc 了解更多信息。
其他资源
除了探索框架本身中的测试用例外,Spring Integration Samples 存储库还有一些专门用于显示测试的示例应用程序,例如testing-examples
和advanced-testing-examples
.
在某些情况下,样本本身具有全面的端到端测试,例如file-split-ftp
样本。