Spring Integration单元测试中AfterReceive方法未调用问题
问题描述
我正在为Springboot应用添加Advice,用于处理目录无文件的场景:当目录为空时停止应用。以下是相关代码:
配置类
@Configuration public class FlowConfiguration { private static final Logger LOGGER = LoggerFactory.getLogger(FlowConfiguration.class); @Bean("creFileInboundChannel") MessageChannel fileInputChannel() { return new DirectChannel(); } @Bean(name = PollerMetadata.DEFAULT_POLLER) public PollerMetadata poller() { return Pollers.fixedRate(1000) .advice(emptyFolderAdvice()) .get(); } @Bean DirectoryScanner directoryScanner(@Value("${inbound.filename.regex:.*}") String regex) { RecursiveDirectoryScanner scanner = new RecursiveDirectoryScanner(); CompositeFileListFilter filter = new CompositeFileListFilter<>( Arrays.asList(new AcceptOnceFileListFilter<>(), new RegexPatternFileListFilter(regex)) ); scanner.setFilter(filter); return scanner; } @Bean @InboundChannelAdapter(value = "creFileInboundChannel", poller = @Poller(fixedDelay = "1000")) MessageSource<File> fileReadingMessageSource(DirectoryScanner directoryScanner, CreConfiguration creConfiguration) { FileReadingMessageSource source = new FileReadingMessageSource(); source.setDirectory(new File(creConfiguration.getInputDir())); source.setScanner(directoryScanner); return source; } @Bean @ServiceActivator(inputChannel = "creOutputChannel") MessageHandler logOutputCreFileHandler() { return message -> { if (message.getPayload() instanceof Optional) { Optional<File> filteredFile = (Optional<File>) message.getPayload(); if (filteredFile.isPresent()) { LOGGER.info("end of process {}, writing file in the output directory", filteredFile.get()); } } }; } @Bean @ServiceActivator(inputChannel = "creFileInboundChannel") MessageHandler creFileInFolderHandler(TraitementCreService traitementCreService) { List<Advice> advices = new ArrayList<>(); advices.add(emptyFolderAdvice()); ServiceActivatingHandler serviceActivatingHandler = new ServiceActivatingHandler(traitementCreService, "invoke"); serviceActivatingHandler.setOutputChannelName("creOutputChannel"); serviceActivatingHandler.setAdviceChain(advices); return serviceActivatingHandler; } @Bean @ServiceActivator(inputChannel = "errorChannel") MessageHandler erreurHandler() { return message -> LOGGER.info("Error when treating file : {}", ((ErrorMessage) message).getOriginalMessage().getPayload()); } @Bean("errorChannel") QueueChannel errorChannel() { return new QueueChannel(500); } @Bean public EmptyFolderAdvice emptyFolderAdvice() { return new EmptyFolderAdvice(); } }
Advice类
public class EmptyFolderAdvice extends AbstractMessageSourceAdvice { @Override public Message<?> afterReceive(Message<?> message, MessageSource<?> source) { File directory = (File) source; File[] files = directory.listFiles(); if (files == null || files.length == 0) { System.exit(0); // Stop the application if no files exist } return message; } }
测试类
@SpringIntegrationTest @SpringBootTest @ContextConfiguration(classes = FiltrageApp.class)//{FlowConfiguration.class, FiltrageConfiguration.class}) public class StopApplicationTest { @MockBean private EmptyFolderAdvice advice; // Inject the advice under test @Qualifier("creFileInboundChannel") @Autowired private MessageChannel inputChannel; @MockBean TraitementCreService traitementCreService; @Autowired private MessageSource<?> messageSource; @Autowired private ApplicationContext context; @Autowired private MessageHandler creFileInFolderHandler; @Test public void testAfterReceiveIsCalled() throws TechnicalException { // Create a test message Message<?> message = MessageBuilder.withPayload("").build(); // Send the message to the input channel //traitementCreService.invoke(null); inputChannel.send(message); traitementCreService.invoke(null); // Verify that the afterReceive method is called Mockito.verify(advice).afterReceive(Mockito.any(Message.class), Mockito.any(MessageSource.class)); // You can also verify the number of times the method is called, e.g., Mockito.verify(mockAdvice, Mockito.times(1)).afterReceive(...) } }
问题:单元测试中,EmptyFolderAdvice的afterReceive方法始终未被调用,请求解决。
解决方案
1. 核心问题分析
- MockBean未被实际注入:配置类中
poller()和creFileInFolderHandler()方法直接调用emptyFolderAdvice()创建实例,没有依赖Spring容器的Bean,导致你用@MockBean替换的实例根本没被用到,容器里运行的还是原始的EmptyFolderAdvice实例。 - 测试逻辑错误:你的Advice是绑定在
MessageSource的Poller上的(用于FileReadingMessageSource的轮询触发),但测试中手动发送消息到inputChannel、调用traitementCreService.invoke()都不会触发MessageSource的轮询流程,自然不会调用afterReceive方法。 - Advice类存在类型转换错误:
File directory = (File) source;这里source是FileReadingMessageSource实例,不是File,直接强转会抛出异常。
2. 代码修正步骤
(1)修改配置类,依赖容器中的Advice实例
把所有直接调用emptyFolderAdvice()的地方,改成通过方法参数注入容器中的Bean:
@Configuration public class FlowConfiguration { // ... 其他代码不变 @Bean(name = PollerMetadata.DEFAULT_POLLER) public PollerMetadata poller(EmptyFolderAdvice emptyFolderAdvice) { // 注入容器中的Advice return Pollers.fixedRate(1000) .advice(emptyFolderAdvice) .get(); } // ... 其他代码不变 @Bean @ServiceActivator(inputChannel = "creFileInboundChannel") MessageHandler creFileInFolderHandler(TraitementCreService traitementCreService, EmptyFolderAdvice emptyFolderAdvice) { // 注入容器中的Advice List<Advice> advices = new ArrayList<>(); advices.add(emptyFolderAdvice); ServiceActivatingHandler serviceActivatingHandler = new ServiceActivatingHandler(traitementCreService, "invoke"); serviceActivatingHandler.setOutputChannelName("creOutputChannel"); serviceActivatingHandler.setAdviceChain(advices); return serviceActivatingHandler; } // ... 其他代码不变 }
(2)修复Advice类的类型转换错误
public class EmptyFolderAdvice extends AbstractMessageSourceAdvice { @Override public Message<?> afterReceive(Message<?> message, MessageSource<?> source) { // 正确获取目录对象 FileReadingMessageSource fileSource = (FileReadingMessageSource) source; File directory = fileSource.getDirectory(); if (directory == null || directory.listFiles() == null || directory.listFiles().length == 0) { System.exit(0); // Stop the application if no files exist } return message; } }
(3)修正测试类逻辑,触发MessageSource的轮询流程
直接调用messageSource.receive()方法,触发Advice的afterReceive执行:
@SpringIntegrationTest @SpringBootTest @ContextConfiguration(classes = FiltrageApp.class) public class StopApplicationTest { @MockBean private EmptyFolderAdvice emptyFolderAdvice; @Autowired private MessageSource<?> messageSource; @Test public void testAfterReceiveIsCalled() { // 触发MessageSource的receive方法,会绑定执行Advice messageSource.receive(); // 验证afterReceive方法被调用 Mockito.verify(emptyFolderAdvice).afterReceive(Mockito.any(Message.class), Mockito.eq(messageSource)); } }
内容的提问来源于stack exchange,提问作者Ares
相关产品推荐
相关产品推荐

