对于最新稳定版本,请使用 Spring Integration 7.0.0spring-doc.cadn.net.cn

测试支持

Spring Integration 提供了多种工具和注释,帮助你测试应用。 测试支持由两个模块提供:spring-doc.cadn.net.cn

Spring-集成-测试-支持 (Spring积分测试在5.0之前的版本中,)提供了基础的独立工具、规则和匹配器用于单元测试。 (它也不依赖于 Spring 集成本身,并在 Framework 测试中内部使用。)Spring积分测试旨在帮助集成测试,提供全面的高级 API 以模拟集成组件并验证单个组件的行为,包括整个集成流程或仅部分集成流程。spring-doc.cadn.net.cn

对企业内测试的全面讨论超出了本参考手册的范围。 请参阅 Gregor Hohpe 和 Wendy Istvanick 撰写的《企业集成项目中的测试驱动开发》论文,获取测试目标集成解决方案的灵感和原则。spring-doc.cadn.net.cn

Spring集成测试框架和测试工具完全基于现有的JUnit、Hamcrest和Mockito库。 应用上下文交互基于 Spring 测试框架。 更多信息请参见相关项目的文档。spring-doc.cadn.net.cn

多亏了Spring集成框架中EIP的规范实现及其一流公民(如消息频道,端点消息处理器),抽象和松耦合原理,你可以实现任何复杂度的积分解决方案。 通过Spring Integration API的流程定义,你可以改进、修改甚至替换流程的部分,而不会影响集成解决方案中的(大多数)其他组件。 测试此类集成方案仍是一项挑战,无论是端到端方法还是隔离方法。 现有的多种工具可以帮助测试或模拟某些集成协议,并且它们与 Spring Integration 通道适配器配合良好。 此类工具的例子包括以下几种:spring-doc.cadn.net.cn

这些工具和库大多用于 Spring 集成测试。此外,来自 GitHub 仓库(在测试每个模块的目录),你可以发现如何构建自己测试以实现集成解决方案的想法。spring-doc.cadn.net.cn

本章其余部分介绍了 Spring Integration 提供的测试工具和实用工具。spring-doc.cadn.net.cn

测试工具

Spring-集成-测试-支持模块提供单元测试所需的工具和辅助工具。spring-doc.cadn.net.cn

TestUtils

TestUtils类主要用于 JUnit 测试中的属性断言,如下示例所示:spring-doc.cadn.net.cn

@Test
public void loadBalancerRef() {
    MessageChannel channel = channels.get("lbRefChannel");
    LoadBalancingStrategy lbStrategy = TestUtils.getPropertyValue(channel,
                 "dispatcher.loadBalancingStrategy", LoadBalancingStrategy.class);
    assertTrue(lbStrategy instanceof SampleLoadBalancingStrategy);
}

TestUtils.getPropertyValue()基于斯普林的DirectFieldAccessor并提供了从目标私有属性获取取值的能力。如前例所示,它还支持通过虚点符号进行嵌套属性访问。spring-doc.cadn.net.cn

createTestApplicationContext()工厂方法产生测试应用上下文实例中附带的 Spring 集成环境。spring-doc.cadn.net.cn

参见其他的JavadocTestUtils关于该类的更多信息。spring-doc.cadn.net.cn

唯一一次触发

唯一一次触发当您需要只产生一条测试消息并验证行为而不影响其他周期消息时,这对轮询端点非常有用。以下示例展示了如何配置唯一一次触发:spring-doc.cadn.net.cn

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

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

以下示例展示了如何使用上述配置唯一一次触发用于测试:spring-doc.cadn.net.cn

@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规则与条件

LongRunningIntegrationTestJUnit 4测试规则用于指示是否应在RUN_LONG_INTEGRATION_TESTS环境或系统属性设置为true. 否则,会被跳过。出于同样的原因,自5.1版本起,@LongRunningTestJUnit 5测试提供了条件注释。spring-doc.cadn.net.cn

哈姆克雷斯特和莫奇托匹配者

org.springframework.integration.test.matcherpackage 包含匹配器要断言的实现消息以及其在单元测试中的属性。以下示例展示了如何使用这样的匹配器(有效载量匹配器):spring-doc.cadn.net.cn

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

模拟消息匹配者工厂可用于 stubbing 和验证的模拟文件,如下示例所示:spring-doc.cadn.net.cn

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

断言J条件和谓词

从5.2版本开始,消息谓词被引入用于断言比赛()断言。 它需要消息对象作为期望。OT还可以配置头部,排除期望和实际声明消息。spring-doc.cadn.net.cn

Spring集成与测试上下文

通常,Spring 应用的测试使用 Spring 测试框架。由于 Spring Integration 基于 Spring Framework 基础,我们能用 Spring 测试框架做的所有作同样适用于测试集成流程。 这org.springframework.integration.test.contextpackage 提供了一些组件,用于增强测试上下文以满足集成需求。首先,我们将测试类配置为@SpringIntegrationTest注释以启用 Spring 集成测试框架,如下示例所示:spring-doc.cadn.net.cn

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

    @Autowired
    private MockIntegrationContext mockIntegrationContext;

}

@SpringIntegrationTest注释填充于模拟集成上下文你可以自动布线到测试类以访问其方法。使用无自动启动Spring集成测试框架防止了通常的端点autoStartup=true从开始。端点与提供的模式匹配,这些模式支持以下简单模式样式:xxx*,xxx,*xxxxxx*yyy.spring-doc.cadn.net.cn

当我们希望不通过入站通道适配器(例如 AMQP 入站网关、JDBC 轮询通道适配器、客户端模式的 WebSocket 消息生成器等)与目标系统建立实际连接时,这非常有用。spring-doc.cadn.net.cn

@SpringIntegrationTest荣誉org.springframework.test.context.NestedTestConfiguration语义,因此它可以在外类(甚至其超类)上声明——@SpringIntegrationTest环境将被继承使用。@Nested测试。spring-doc.cadn.net.cn

模拟集成上下文旨在用于实际应用环境中对豆子进行修改的目标测试用例。 例如,端点自动启动被覆盖为false可以用 mocks 替代,如下示例所示:spring-doc.cadn.net.cn

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

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

    Message<?> receive = this.results.receive(10_000);
    assertNotNull(receive);
}
我的源端点这里指的是豆子名称SourcePollingChannelAdapter我们替换实数消息源和我们的模拟一起。 同样,MockIntegrationContext.substituteMessageHandlerFor()期望为集成消费者,包裹了一个消息处理器作为终点。

测试完成后,你可以将端点豆的状态恢复到真实配置,使用MockIntegrationContext.resetBeans():spring-doc.cadn.net.cn

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

更多信息请参见 Javadocspring-doc.cadn.net.cn

集成模拟

org.springframework.integration.test.mock该软件包提供了用于 Spring 集成组件活动的模拟、存根和验证的工具和工具。 模拟功能完全基于并兼容著名的 Mockito 框架。 (目前 Mockito 传递依赖于 2.5.x 或更高版本。)spring-doc.cadn.net.cn

模拟集成

模拟集成factory 提供了一个 API,用于构建 Spring 集成 beans 的 mock,这些 democks 是集成流程的一部分 (消息源,消息制作人,消息处理器消息频道). 你可以在配置阶段以及目标测试方法中使用目标模拟,在进行验证和断言前替换真实端点,如下示例所示:spring-doc.cadn.net.cn

<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-doc.cadn.net.cn

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

为此,上述模拟集成上下文应从测试中使用,如下示例所示:spring-doc.cadn.net.cn

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

与Mockito不同消息源模拟对象,模拟消息处理器是正则摘要消息制作处理程序通过链式API扩展来处理收到的消息存根。 这模拟消息处理器提供handleNext(Consumer<Message<?>>)指定下一条请求消息的单向存根。 它用于模拟那些不产生回复的消息处理程序。 这handleNextAndReply(Function<Message<?>, ?>)为下一条请求消息执行相同的存根逻辑并生成回复而提供。 它们可以串联起来,模拟所有预期请求消息变体的任何任意请求-回复场景。 这些消费者和函数会从堆栈中一个接一个地应用到收到的消息上,直到最后一个,然后用于所有剩余的消息。 其行为与莫奇托相似doReturn()应用程序接口。spring-doc.cadn.net.cn

此外,你还可以提供一块Mockito。ArgumentCaptor<Message<?>>前往模拟消息处理器在构造子参数中。 每个请求消息模拟消息处理器被捕获争论者. 在测试过程中,你可以使用它getValue()getAllValues()验证和断言这些请求消息的方法。spring-doc.cadn.net.cn

模拟集成上下文提供substituteMessageHandlerFor()可以替换实际配置的API。消息处理器其中模拟消息处理器在被测的端点。spring-doc.cadn.net.cn

以下示例展示了一个典型的使用场景:spring-doc.cadn.net.cn

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());
常规消息处理器嘲讽(或模拟消息处理器即使是 也必须使用ReactiveStreamsConsumer其中响应式消息处理器配置。

参见模拟集成模拟消息处理器更多信息请参见Javadoc。spring-doc.cadn.net.cn

其他资源

除了探索框架内的测试用例外,Spring集成示例仓库还专门展示测试过程,例如:测试示例高级测试示例. 在某些情况下,样本本身还包含全面的端到端测试,例如文件拆分-FTP样本。spring-doc.cadn.net.cn