You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:17:39