此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Integration 6.3.1Spring中文文档

此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Integration 6.3.1Spring中文文档

Spring Integration 提供了许多实用程序和注释来帮助您测试应用程序。 测试支持由两个模块提供:Spring中文文档

  • spring-integration-test-support包含核心项目和共享实用程序Spring中文文档

  • spring-integration-test为集成测试提供模拟和应用程序上下文配置组件Spring中文文档

spring-integration-test-support (spring-integration-test在 5.0 之前的版本中)为单元测试提供基本的独立实用程序、规则和匹配器。 (它也不依赖于 Spring Integration 本身,并且在框架测试中内部使用)。 旨在帮助进行集成测试,并提供全面的高级 API 来模拟集成组件并验证单个组件的行为,包括整个集成流或仅部分集成流。spring-integration-testSpring中文文档

对企业测试的彻底处理超出了本参考手册的范围。 请参阅 Gregor Hohpe 和 Wendy Istvanick 撰写的“企业集成项目中的测试驱动开发”一文,了解测试目标集成解决方案的思想和原则的来源。Spring中文文档

Spring 集成测试框架和测试实用程序完全基于现有的 JUnit、Hamcrest 和 Mockito 库。 应用程序上下文交互基于 Spring 测试框架。 有关详细信息,请参阅这些项目的文档。Spring中文文档

由于 Spring 集成框架中 EIP 的规范实现及其一流的公民(如 、 和 )、抽象和松散耦合原则,您可以实现任何复杂程度的集成解决方案。 使用用于流定义的 Spring Integration API,您可以改进、修改甚至替换流的某些部分,而不会影响(大部分)集成解决方案中的其他组件。 测试这样的集成解决方案仍然是一个挑战,无论是从端到端方法还是从隔离方法。 一些现有的工具可以帮助测试或模拟一些集成协议,它们可以很好地与Spring Integration通道适配器配合使用。 此类工具的示例包括:MessageChannelEndpointMessageHandlerSpring中文文档

  • Spring 及其可用于测试 HTTP。MockMVCMockRestServiceServerSpring中文文档

  • 一些 RDBMS 供应商为 JDBC 或 JPA 支持提供嵌入式数据库。Spring中文文档

  • 可以嵌入 ActiveMQ 以测试 JMS 或 STOMP 协议。Spring中文文档

  • 有嵌入式 MongoDB 和 Redis 的工具。Spring中文文档

  • Tomcat 和 Jetty 具有嵌入式库来测试真正的 HTTP、Web 服务或 WebSocket。Spring中文文档

  • Apache Mina 项目中的 and 可用于测试 FTP 和 SFTP 协议。FtpServerSshServerSpring中文文档

  • Hazelcast 可以在测试中作为真实数据网格节点运行。Spring中文文档

  • 策展人框架为 Zookeeper 提供了交互。TestingServerSpring中文文档

  • Apache Kafka 提供了在测试中嵌入 Kafka Broker 的管理工具。Spring中文文档

  • GreenMail 是一个开源、直观且易于使用的电子邮件服务器测试套件,用于测试目的。Spring中文文档

这些工具和库中的大多数都用于 Spring Integration 测试。 此外,从 GitHub 存储库(在每个模块的目录中)中,您可以发现有关如何为集成解决方案构建自己的测试的想法。testSpring中文文档

本章的其余部分将介绍 Spring Integration 提供的测试工具和实用程序。Spring中文文档

测试实用程序

该模块为单元测试提供实用程序和帮助程序。spring-integration-test-supportSpring中文文档

测试工具

该类主要用于 JUnit 测试中的属性断言,如以下示例所示:TestUtilsSpring中文文档

@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 的,并提供从目标私有属性获取值的能力。 如前面的示例所示,它还支持使用点表示法访问嵌套属性。DirectFieldAccessorSpring中文文档

工厂方法使用提供的 Spring Integration 环境生成实例。createTestApplicationContext()TestApplicationContextSpring中文文档

有关此类的更多信息,请参阅其他方法的 JavadocTestUtilsSpring中文文档

OnlyOnceTrigger

当只需要生成一条测试消息并验证行为而不影响其他周期消息时,OnlyOnceTrigger 可用于轮询终结点。 以下示例演示如何配置:OnlyOnceTriggerSpring中文文档

<bean id="testTrigger" class="org.springframework.integration.test.util.OnlyOnceTrigger" />

<int:poller id="jpaPoller" trigger="testTrigger">
    <int:transactional transaction-manager="transactionManager" />
</int:poller>

以下示例演示如何使用上述配置进行测试:OnlyOnceTriggerSpring中文文档

@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 测试规则,用于指示在环境或系统属性设置为 时是否应运行测试。 否则,将跳过它。 出于同样的原因,从 5.1 版开始,为 JUnit 5 测试提供了条件注释。LongRunningIntegrationTestRUN_LONG_INTEGRATION_TESTStrue@LongRunningTestSpring中文文档

Hamcrest 和 Mockito Matchers

该包包含多个要断言的实现及其在单元测试中的属性。 下面的示例演示如何使用一个这样的匹配器 ():org.springframework.integration.test.matcherMatcherMessagePayloadMatcherSpring中文文档

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)));
}

工厂可用于模拟存根和验证,如以下示例所示:MockitoMessageMatchersSpring中文文档

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));

AssertJ 条件和谓词

从版本 5.2 开始,引入了用于 AssertJ 断言的 。 它需要一个对象作为期望。 并且还可以配置标头以从期望中排除以及从实际消息中排除以断言。MessagePredicatematches()MessageSpring中文文档

Spring 集成和测试上下文

通常,Spring 应用程序的测试使用 Spring 测试框架。 由于 Spring Integration 是基于 Spring Framework 基础的,因此我们在测试集成流程时也可以使用 Spring Test Framework 执行的所有操作。 该软件包提供了一些组件,用于增强测试上下文以满足集成需求。 首先,我们使用注释配置测试类以启用 Spring Integration Test Framework,如以下示例所示:org.springframework.integration.test.context@SpringIntegrationTestSpring中文文档

@SpringJUnitConfig
@SpringIntegrationTest(noAutoStartup = {"inboundChannelAdapter", "*Source*"})
public class MyIntegrationTests {

    @Autowired
    private MockIntegrationContext mockIntegrationContext;

}

注释填充一个 Bean,您可以自动连接到测试类以访问其方法。 使用该选项,Spring Integration Test Framework 会阻止正常启动的端点。 端点与提供的模式匹配,这些模式支持以下简单模式样式:、*xxx 和 。@SpringIntegrationTestMockIntegrationContextnoAutoStartupautoStartup=truexxx*xxxxxx*yyySpring中文文档

当我们不希望从入站通道适配器(例如 AMQP 入站网关、JDBC 轮询通道适配器、客户端模式下的 WebSocket 消息生成器等)与目标系统建立实际连接时,这很有用。Spring中文文档

旨在用于目标测试用例,以便在实际应用程序上下文中修改 Bean。 例如,可以将重写的终结点替换为 mock,如以下示例所示:MockIntegrationContextautoStartupfalseSpring中文文档

@Test
public void testMockMessageSource() {
    MessageSource<String> messageSource = () -> new GenericMessage<>("foo");

    this.mockIntegrationContext.substituteMessageSourceFor("mySourceEndpoint", messageSource);

    Message<?> receive = this.results.receive(10_000);
    assertNotNull(receive);
}
这里指的是 的 bean 名称,我们用 mock 替换了 real。 类似地,期望 的 Bean 名称将 包装成端点。mySourceEndpointSourcePollingChannelAdapterMessageSourceMockIntegrationContext.substituteMessageHandlerFor()IntegrationConsumerMessageHandler

执行测试后,您可以使用以下方法将端点 Bean 的状态恢复到实际配置:MockIntegrationContext.resetBeans()Spring中文文档

@After
public void tearDown() {
    this.mockIntegrationContext.resetBeans();
}

从版本 6.3 开始,引入了 API。 这可用于替换 . 例如,生产配置可能依赖于每日(甚至每周)cron 计划。 可以将任何自定义注入到目标终结点中,以缩短时间跨度。 例如,上面提到的 OnlyOnceTrigger 建议立即安排轮询任务并仅执行一次。MockIntegrationContext.substituteTriggerFor()TriggerAbstractPollingEndpointTriggerSpring中文文档

有关更多信息,请参见 JavadocSpring中文文档

这里指的是 的 bean 名称,我们用 mock 替换了 real。 类似地,期望 的 Bean 名称将 包装成端点。mySourceEndpointSourcePollingChannelAdapterMessageSourceMockIntegrationContext.substituteMessageHandlerFor()IntegrationConsumerMessageHandler

集成模拟

该软件包提供了用于模拟、存根和验证 Spring Integration 组件上活动的工具和实用程序。 mocking 功能完全基于众所周知的 Mockito 框架并与之兼容。 (当前的 Mockito 传递依赖项是 2.5.x 或更高版本。org.springframework.integration.test.mockSpring中文文档

模拟集成

工厂提供了一个 API,用于为 Spring Integration bean 构建模拟,这些 bean 是集成流 (, , 和 ) 的一部分。 您可以在配置阶段以及目标测试方法中使用目标模拟,在执行验证和断言之前替换实际终结点,如以下示例所示:MockIntegrationMessageSourceMessageProducerMessageHandlerMessageChannelSpring中文文档

<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 配置来实现与上一个示例相同的配置:Spring中文文档

@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();

为此,应从测试中使用上述内容,如以下示例所示:MockIntegrationContextSpring中文文档

this.mockIntegrationContext.substituteMessageSourceFor("mySourceEndpoint",
        MockIntegration.mockMessageSource("foo", "bar", "baz"));
Message<?> receive = this.results.receive(10_000);
assertNotNull(receive);
assertEquals("FOO", receive.getPayload());

与 Mockito 模拟对象不同,它是一个常规扩展,带有一个链 API,用于处理传入消息的存根。 提供为下一个请求消息指定单向存根。 它用于模拟不生成回复的消息处理程序。 用于对下一个请求消息执行相同的存根逻辑并为其生成回复。 它们可以链接起来,以模拟所有预期请求消息变体的任何任意请求-答复方案。 这些使用者和函数将应用于传入消息,一次从堆栈中传入一条,直到最后一条,然后用于所有剩余消息。 该行为类似于 Mockito 或 API。MessageSourceMockMessageHandlerAbstractMessageProducingHandlerMockMessageHandlerhandleNext(Consumer<Message<?>>)handleNextAndReply(Function<Message<?>, ?>)AnswerdoReturn()Spring中文文档

此外,还可以向 in 构造函数参数提供 Mockito。 的每个请求消息都由 . 在测试期间,可以使用 its 和 方法来验证和断言这些请求消息。ArgumentCaptor<Message<?>>MockMessageHandlerMockMessageHandlerArgumentCaptorgetValue()getAllValues()Spring中文文档

它提供了一个 API,允许您将实际配置替换为受测端点中的 a。MockIntegrationContextsubstituteMessageHandlerFor()MessageHandlerMockMessageHandlerSpring中文文档

以下示例显示了一个典型的使用场景:Spring中文文档

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());
常规的模拟(或)也必须用于具有配置的配置。MessageHandlerMockMessageHandlerReactiveStreamsConsumerReactiveMessageHandler

有关更多信息,请参见 MockIntegrationMockMessageHandler Javadoc。Spring中文文档

常规的模拟(或)也必须用于具有配置的配置。MessageHandlerMockMessageHandlerReactiveStreamsConsumerReactiveMessageHandler

其他资源

除了探索框架本身的测试用例外,Spring Integration Samples 存储库还有一些专门用于显示测试的示例应用程序,例如 和 . 在某些情况下,样品本身具有全面的端到端测试,例如样品。testing-examplesadvanced-testing-examplesfile-split-ftpSpring中文文档