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

WebFlux单元测试问题:调用未进入.map与.flatMap处理逻辑

WebFlux Mono链未执行的单元测试问题

我正在为WebFlux中的calculate方法编写单元测试,业务代码如下:

@Override
public Mono<SearchSessionAndETAResponse> calculate(DTO sessionRequest) throws ExecutionException, InterruptedException {
    List<GeospatialData> dataFromRedis =  getDataFromRedis(sessionRequest.getSessionId());
    return Mono.just(dataFromRedis)
            .map(data -> {
                System.out.println("data::"+data);
                return "entityIdentifiers";
            })
            .flatMap(entityIdentifiers -> {
                System.out.println("entityIdentifiers::"+entityIdentifiers);
                return etaService.entitiesEta("entityIdentifiers");
            })
            .map(wayPointRoute -> {
                return searchSessionAndETAResponse;
            });
}

对应的测试用例代码:

@ExtendWith(MockitoExtension.class)
public class BroadCastingServiceImplTest {

    @InjectMocks
    private BroadCastingServiceImpl broadCastingService;
    @Mock
    private EtaService etaService;

    @BeforeEach
    void setUp() {
        closeable = MockitoAnnotations.openMocks(this);
    }

    @AfterEach
    void tearDown() throws Exception {
        closeable.close();
    }

    @Test
    @DisplayName("Given a valid request, when SearchSession and calculate eta is called, it should search Broadcasting session and return eta")
    void givenValidRequest_whenSearchSessionAndCalculateEta_shouldReturnBroadCastingResponseDTO() throws ExecutionException, InterruptedException, IOException {
        // GIVEN
        UUID sessionId = UUID.randomUUID();
        DTO searchSessionAndCalculateETA = new DTO();
        searchSessionAndCalculateETA.setSessionId(sessionId.toString());
        searchSessionAndCalculateETA.setWaypoints(List.of("waypointsDTO", "waypointsDTO2"));
        given(etaService.entitiesEta("entitiesEtaRequestDTO")).willReturn(Mono.just(List.of("entitiesEtaResponseDTO")));

        // WHEN
        Mono<SearchSessionAndETAResponse> result = broadCastingService.searchSessionAndCalculateETA(searchSessionAndCalculateETA);
    }
}

问题:运行测试用例时,尽管dataFromRedis不为空,但调用并未进入Mono链中的.map、.flatMap及后续.map处理逻辑,请问我遗漏了什么关键步骤?


问题原因与解决方法
  • Mono是惰性执行的:WebFlux的响应式类型(Mono/Flux)遵循惰性求值原则,只有当被订阅时,整个链式调用才会实际执行。你当前的测试仅创建了Mono对象,但未触发订阅操作,导致链中的.map、.flatMap等逻辑完全没启动。
  • 测试需触发订阅或使用StepVerifier:单元测试中推荐使用StepVerifier(来自spring-boot-test依赖)来验证响应式流的行为,它会自动触发订阅,并能断言流的输出、完成状态等。如果不想用StepVerifier,也可以调用result.block()手动阻塞触发订阅,但这种方式不符合响应式编程的测试规范,仅作临时调试用。
  • 额外问题:Mock参数不匹配:业务代码中调用etaService.entitiesEta("entityIdentifiers")时传入的是固定字符串"entityIdentifiers",但测试中Mock的却是etaService.entitiesEta("entitiesEtaRequestDTO"),参数不匹配会导致Mock失效,即使触发订阅,这一步也会返回空或报错。

修改后的测试代码示例:

@Test
@DisplayName("Given a valid request, when SearchSession and calculate eta is called, it should search Broadcasting session and return eta")
void givenValidRequest_whenSearchSessionAndCalculateEta_shouldReturnBroadCastingResponseDTO() throws ExecutionException, InterruptedException, IOException {
    // GIVEN
    UUID sessionId = UUID.randomUUID();
    DTO searchSessionAndCalculateETA = new DTO();
    searchSessionAndCalculateETA.setSessionId(sessionId.toString());
    searchSessionAndCalculateETA.setWaypoints(List.of("waypointsDTO", "waypointsDTO2"));
    
    // 确保Mock参数与业务代码一致
    given(etaService.entitiesEta("entityIdentifiers")).willReturn(Mono.just(List.of("entitiesEtaResponseDTO")));
    
    // 初始化预期返回的响应对象
    SearchSessionAndETAResponse expectedResponse = new SearchSessionAndETAResponse();

    // WHEN & THEN:用StepVerifier触发订阅并验证结果
    StepVerifier.create(broadCastingService.searchSessionAndCalculateETA(searchSessionAndCalculateETA))
            .expectNext(expectedResponse)
            .verifyComplete();
}

内容的提问来源于stack exchange,提问作者Divya Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 19:44:54