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