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

Spring Boot 3.1.3+WebFlux下StepVerifier测试Reactive方法最佳实践

Spring WebFlux 单元测试:StepVerifier 实践与问题解决

问题根源分析

你的测试未触发HTTP请求,核心原因有两点:

  1. 方法可见性限制:sendMessage是private方法,单元测试无法直接正确触发其内部逻辑;
  2. 未Mock外部依赖:直接使用真实WebClient时,要么因无可用服务端导致请求失败,要么因依赖未正确初始化导致流未执行。

解决方案:Mock WebClient + StepVerifier 验证

1. 调整方法可见性(可选但推荐)

将sendMessage改为protected或public,或者通过服务类的public入口方法间接调用它,确保测试能覆盖目标逻辑。

2. Mock WebClient 调用链

WebClient采用链式调用,需依次Mock每个步骤的返回对象,模拟请求成功/失败场景。以下是完整测试示例:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import java.net.URI;

import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class MessageServiceTest {

    @Mock
    private WebClient oauth2WebClientPnt;

    @Mock
    private WebClient.RequestBodyUriSpec requestBodyUriSpec;

    @Mock
    private WebClient.RequestHeadersSpec<?> requestHeadersSpec;

    @Mock
    private WebClient.ResponseSpec responseSpec;

    @InjectMocks
    private MessageService messageService;

    @Test
    void sendMessage_ShouldReturnTrue_WhenRequestSucceeds() {
        String testUri = "http://test.example.com";
        String testMsg = "{\"content\":\"test\"}";
        URI expectedUri = URI.create(testUri);

        // 构建WebClient调用链Mock
        when(oauth2WebClientPnt.post()).thenReturn(requestBodyUriSpec);
        when(requestBodyUriSpec.uri(eq(expectedUri))).thenReturn(requestBodyUriSpec);
        when(requestBodyUriSpec.body(eq(BodyInserters.fromValue(testMsg)))).thenReturn(requestHeadersSpec);
        when(requestHeadersSpec.headers(any())).thenReturn(requestHeadersSpec);
        when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
        when(responseSpec.bodyToMono(Boolean.class)).thenReturn(Mono.just(true));

        // 调用目标方法
        Mono<Boolean> result = messageService.sendMessage(testUri, testMsg);

        // StepVerifier验证流行为
        StepVerifier.create(result)
                .expectNext(true)
                .verifyComplete();
    }

    @Test
    void sendMessage_ShouldThrowException_WhenUriInvalid() {
        String invalidUri = "invalid-uri-format";
        String testMsg = "{\"content\":\"test\"}";

        Mono<Boolean> result = messageService.sendMessage(invalidUri, testMsg);

        StepVerifier.create(result)
                .expectError(RuntimeException.class)
                .verify();
    }
}

Reactive 函数测试最佳实践

  • 聚焦Public API:单元测试优先覆盖服务对外暴露的public方法,而非内部私有方法,确保测试贴近实际使用场景。
  • 用StepVerifier替代block():StepVerifier会自动订阅响应式流,同时验证流的完整生命周期(成功值、错误信号、完成信号),避免阻塞线程。
  • 强制Mock外部依赖:对WebClient、数据库客户端等外部依赖必须Mock,保证测试不依赖第三方服务,提升稳定性和执行速度。
  • 覆盖边界场景:测试无效参数、服务错误响应、空输入等边界情况,确保代码异常处理逻辑生效。
  • 验证副作用:如果方法包含日志、数据库写入等副作用,用Mockito的verify()方法确认这些操作已执行。

内容的提问来源于stack exchange,提问作者Angelo Immediata

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 21:30:12