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

如何为Spring Integration DSL配置、网关、控制器编写JUnit5测试用例

Spring Integration Scatter-Gather 流JUnit测试实现

先提前修正原代码里会导致测试启动失败的问题:

  • 配置类里注入的是LoansServiceImpl loansService,但三个子流逻辑里调用的是lionService,需要把注入实例改成实际使用的LionService类型,不然启动会报找不到Bean的错误。
  • 主流程里的.convert(LoanProvisionRequest.class)和后面子流接收的LionRequest类型不匹配,按实际入参类型调整,不然会抛类型转换异常。
  • 配置类里直接初始化dbId的写法有问题,所有请求会共用同一个ID,把ID生成逻辑移到flow1的处理方法里,按请求维度单独生成。

前置依赖

如果是Spring Boot项目,引入测试starter即可,不需要额外加其他依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-test</artifactId>
    <scope>test</scope>
</dependency>

1. 集成流+网关层测试

这层测试加载完整Spring集成上下文,Mock掉业务服务依赖,直接验证Scatter-Gather的并行调用、结果聚合逻辑,不需要启动Web容器:

@SpringBootTest
@SpringIntegrationTest
class LionFlowTest {

    @Autowired
    private LionGateway lionGateway;

    @MockBean
    private LionService lionService;

    @Test
    void testAllSubFlowExecutedAndResultAggregated() throws JsonProcessingException {
        // 构造测试入参
        LionRequest req = new LionRequest();
        req.setBizId("test-biz-001");
        req.setContent("test payload");

        // 模拟业务方法返回
        doNothing().when(lionService).saveLionRequest(any(LionRequest.class), anyString());
        when(lionService.getData(any(LionRequest.class), eq(SourceSystem.PROVISION))).thenReturn("flow2-return");
        when(lionService.prepareCDRequest(any(Message.class))).thenReturn("flow3-return");

        // 触发流执行
        List<?> aggregateResult = lionGateway.echo(req);

        // 断言:3个recipient子流的消息全部被收集
        assertEquals(3, aggregateResult.size());
        // 验证三个子流的业务方法都被调用过一次
        verify(lionService, times(1)).saveLionRequest(any(), anyString());
        verify(lionService, times(1)).getData(any(), eq(SourceSystem.PROVISION));
        verify(lionService, times(1)).prepareCDRequest(any());
    }

    @Test
    void testSubFlowThrowException() throws JsonProcessingException {
        LionRequest req = new LionRequest();
        doThrow(new JsonProcessingException("json parse fail"){}).when(lionService).saveLionRequest(any(), anyString());
        // 验证子流异常能正常抛出,不会挂住线程
        assertThrows(RuntimeException.class, () -> lionGateway.echo(req));
    }
}

2. 控制器层测试

这层测试只加载Web层上下文,Mock掉网关依赖,验证接口的序列化、响应逻辑,不需要启动完整集成流,执行速度更快:

@WebMvcTest(LionController.class)
class LionControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private LionGateway lionGateway;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void testInvokeEndpointReturnCorrectResponse() throws Exception {
        LionRequest req = new LionRequest();
        req.setBizId("ctrl-test-001");
        String reqJson = objectMapper.writeValueAsString(req);

        // 模拟网关返回聚合结果
        List<String> mockReturn = Arrays.asList("flow1-done", "flow2-done", "flow3-done");
        when(lionGateway.echo(any(LionRequest.class))).thenReturn(mockReturn);

        // 发请求验证
        mockMvc.perform(post("/invoke-integration")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(reqJson))
                .andExpect(status().isOk())
                .andExpect(content().string(mockReturn.toString()));

        verify(lionGateway, times(1)).echo(any(LionRequest.class));
    }
}

测试调优提示

  • 因为流里用了异步线程池,测试如果出现偶发断言失败,不要硬写Thread.sleep,引入Awaitility包做异步结果等待即可;调试阶段可以把所有异步通道临时改成同步通道,排查完逻辑再改回异步配置。
  • 如果需要验证Scatter-Gather自带的序列号、关联ID等消息头,可以直接注入output-flow通道,拉取完整消息对象做断言,不需要只依赖网关返回的payload。
  • 测试环境可以自定义线程池Bean,把核心线程数、最大线程数调小,避免测试时创建过多线程占用资源。

内容的提问来源于stack exchange,提问作者Somnath Mukherjee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 20:24:10