java - MockIntegrationMessage 在缓存上下文时不起作用

标签 java spring spring-boot spring-integration spring-test

我正在使用 Spring Integration 5.1.7.RELEASESpring Boot 2.1.7.RELEASESpring Integration Test 5.0.11 开发一个项目.RELEASE.

作为测试的一部分,我正在尝试单元测试 IntegrationFlow,它执行对不同MessageHandlers 的调用,所以我mocking 它们使用来自 Spring Integration TestMockIntegrationContext

我的代码在我们的 Jenkins 服务器上出现间歇性问题,这似乎与缓存的上下文有关。我有一个没有模拟的集成测试 (@SpringBootTest) 和一个专门针对 IntegrationFlow 的测试类,其中模拟了 MessageHandlers

如果之前运行了集成测试并且缓存了上下文,则 MockIntegrationContext 执行的设置似乎没有效果,底层的 service-activator 被调用,甚至当日志显示它已取消订阅并且模拟已订阅该 channel 时。

下面是轮询器和句柄端点的定义:

@Configuration
public class FlowConfig {

  @Bean
  public IntegrationFlow fileFlow() {
          return IntegrationFlows.from(
                  Files.inboundAdapter(new File(fileDir)).autoCreateDirectory(true).preventDuplicates(false),
                      e -> e.poller(Pollers.fixedDelay(pollingFrequency)).id("fileInboundSourceEndpoint").get())
                  ...
                  .handle(fileParseHandler, e -> e.id("fileParseHandlerEndpoint")) 
                  ...
                  .get()
  }
}

这是测试设置:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = FlowConfig.class)
@SpringIntegrationTest
@EnableIntegration
public class FlowConfigTest {

    @Autowired
    MockIntegrationContext mockIntegrationCtx;

    @Test
    public void test_fileFlow() throws IOException {
        mockIntegrationCtx.substituteMessageHandlerFor("fileParseHandlerEndpoint",
                    MockIntegration.mockMessageHandler().handleNextAndReply(m -> m));
        new File(fileDir + "test").createNewFile();
    }
}

这是两组不同的日志,分别来自一次不成功和一次成功的测试执行:

2019-09-09 15:17:02.059  INFO 14462 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : Removing {service-activator:fileParseHandlerEndpoint} as a subscriber to the 'fileFlow.channel#2' channel
2019-09-09 15:17:02.059  INFO 14462 --- [           main] o.s.integration.channel.DirectChannel    : Channel 'org.springframework.context.support.GenericApplicationContext@51f95f0d.fileFlow.channel#2' has 0 subscriber(s).
2019-09-09 15:17:02.059  INFO 14462 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : stopped fileParseHandlerEndpoint
2019-09-09 15:17:02.062  INFO 14462 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : Adding {message-handler:fileParseHandlerEndpoint} as a subscriber to the 'fileFlow.channel#2' channel
2019-09-09 15:17:02.062  INFO 14462 --- [           main] o.s.integration.channel.DirectChannel    : Channel 'org.springframework.context.support.GenericApplicationContext@51f95f0d.fileFlow.channel#2' has 1 subscriber(s).
2019-09-09 15:17:02.062  INFO 14462 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : started fileParseHandlerEndpoint
...
2019-09-09 15:17:03.731 DEBUG 14462 --- [   scheduling-1] o.s.i.file.FileReadingMessageSource      : Added to queue: [/tmp/file]
2019-09-09 15:17:03.732 DEBUG 14462 --- [   scheduling-1] o.s.i.e.SourcePollingChannelAdapter      : Poll resulted in Message: GenericMessage [payload=/tmp/file, headers={file_originalFile=/tmp/file, id=4323b036-4edd-019e-2c61-6917c7970767, file_name=file, file_relativePath=file, timestamp=1568056623732}]
...
2019-09-09 15:17:03.732 DEBUG 14462 --- [   scheduling-1] o.s.integration.channel.DirectChannel    : preSend on channel 'fileFlow.channel#2', message: GenericMessage [payload=/tmp/file, headers={file_originalFile=/tmp/file, errorChannel=error_channel, id=af997a89-40d6-9024-65fc-7ab3883c329b, file_name=file, file_relativePath=file, timestamp=1568056623732}]
2019-09-09 15:17:03.732 DEBUG 14462 --- [   scheduling-1] o.s.i.handler.ServiceActivatingHandler   : ServiceActivator for [org.springframework.integration.handler.MethodInvokingMessageProcessor@7b4acdc2] (fileParseHandlerEndpoint) received message: GenericMessage [payload=/tmp/file, headers={file_originalFile=/tmp/file, errorChannel=error_channel, id=af997a89-40d6-9024-65fc-7ab3883c329b, file_name=file, file_relativePath=file, timestamp=1568056623732}]
[Fatal Error] file:1:1: Premature end of file.
13:41:05.126 [main] INFO  o.s.i.endpoint.EventDrivenConsumer - Removing {service-activator:fileParseHandlerEndpoint} as a subscriber to the 'fileFlow.channel#2' channel
13:41:05.126 [main] INFO  o.s.i.channel.DirectChannel - Channel 'org.springframework.context.support.GenericApplicationContext@6fb365ed.fileFlow.channel#2' has 0 subscriber(s).
13:41:05.126 [main] INFO  o.s.i.endpoint.EventDrivenConsumer - stopped fileParseHandlerEndpoint
13:41:05.128 [main] INFO  o.s.i.endpoint.EventDrivenConsumer - Adding {message-handler:fileParseHandlerEndpoint} as a subscriber to the 'fileFlow.channel#2' channel
13:41:05.128 [main] INFO  o.s.i.channel.DirectChannel - Channel 'org.springframework.context.support.GenericApplicationContext@6fb365ed.fileFlow.channel#2' has 1 subscriber(s).
13:41:05.128 [main] INFO  o.s.i.endpoint.EventDrivenConsumer - started fileParseHandlerEndpoint
...
13:41:06.775 [task-scheduler-1] DEBUG o.s.i.file.FileReadingMessageSource - Added to queue: [/tmp/file]
13:41:06.775 [task-scheduler-1] DEBUG o.s.i.e.SourcePollingChannelAdapter - Poll resulted in Message: GenericMessage [payload=/tmp/file, headers={file_originalFile=/tmp/file, id=3bb52d11-49ad-3ea8-eb67-7e6da721022a, file_name=file, file_relativePath=file, timestamp=1568050866775}]
...
13:41:06.788 [task-scheduler-1] DEBUG o.s.i.channel.DirectChannel - preSend on channel 'fileFlow.channel#2', message: GenericMessage [payload=/tmp/file, headers={file_originalFile=/tmp/file, errorChannel=error_channel, id=ec6b0213-8109-ae43-ac27-d17b1bbad8aa, file_name=file, file_relativePath=file, timestamp=1568050866777}]
13:41:06.789 [task-scheduler-1] DEBUG o.s.i.test.mock.MockMessageHandler - org.springframework.integration.test.mock.MockMessageHandler$MockitoMock$1634676946@76fb98a2 received message: GenericMessage [payload=/tmp/file, headers={file_originalFile=/tmp/file, errorChannel=error_channel, id=ec6b0213-8109-ae43-ac27-d17b1bbad8aa, file_name=file, file_relativePath=file, timestamp=1568050866777}]

我知道可能有 @DirtiesContext 的组合应该可以解决这个问题,但我仍在努力理解为什么 MockIntegrationContext 无法模拟 MessageHandlers 来自缓存的上下文。

有什么方法可以在不使用 @DirtiesContext 的情况下解决这个问题?

最佳答案

首先,当你使用Spring Boot时,你需要依赖它的版本管理。 此外,当您按原样使用 Spring Integration 时,请考虑使用 spring-integration-bom 进行导入。这样你就不需要为单个模块指定版本。

我的观点是,spring-integration-corespring-integration-test 有不同的版本。他们必须相同。否则,您可能会在运行时遇到一团糟。

现在谈谈这个主题。

我认为这不是缓存问题。

此测试套件的应用程序上下文启动与目标测试方法中的 mockIntegrationCtx.substituteMessageHandlerFor() 之间存在竞争条件。

问题来自于您从 e.poller(Pollers.fixedDelay(pollingFrequency) 开始流程这一事实。这个流程由后台的 TaskScheduler 处理,并且独立于测试套件的主线程生成消息。这就是您观察到意外行为的原因。

您可以通过类似autoStartup 的操作来解决这个问题。 @SpringIntegrationTest 为您准备了这个:

/**
 * Specify a simple matching patterns ("xxx*", "*xxx", "*xxx*" or "xxx*yyy") for
 * {@link org.springframework.integration.endpoint.AbstractEndpoint}
 * bean names to mark them as {@code autoStartup = false}
 * during context initialization.
 * @return the endpoints name patterns to stop during context initialization
 * @see IntegrationEndpointsInitializer
 * @see org.springframework.util.PatternMatchUtils
 */
String[] noAutoStartup() default {};

所以,你这样做:

@SpringIntegrationTest(noAutoStartup = "fileInboundSourceEndpoint")

然后在测试中你自动连接一个 channel 适配器:

@自动连线 private SourcePollingChannelAdapter fileInboundSourceEndpoint;

substituteMessageHandlerFor() 之后启动它:

this.fileInboundSourceEndpoint.start();

关于java - MockIntegrationMessage 在缓存上下文时不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57861091/

相关文章:

java - Junit 测试模拟文件操作

java - Spring boot 中 SQL Server 的多种架构

java - CountDownLatch 在 Java 中如何工作?

java - 如何知道一个 where 子句查询结果包含另一个 where 子句查询结果?

java - 程序将按降序输出三个整数

rest - 测试时出错 : found multiple declaration of @BootstrapWith for test class

具有两种 MVC 配置的 Spring Boot

java - 在 Linux 中作为服务安装的 Spring-Boot 应用程序无法共享 postgresql 数据库

java - Spring 框架 - GET 和 POST 之间的区别

java - Spring Data - MongoDB - 更多与 和 相关的术语