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

WebClient Rest调用中Builder抛出空指针异常(NPE)求助

解决WebClient.Builder Mock时的空指针异常(NPE)

问题背景

在为通过WebClient.Builder构造WebClient的Service类编写测试时,已用Mockito Mock了WebClient.Builder,但构造Service实例时抛出空指针异常,提示builder为null,同时后续测试逻辑也无法正确Stub WebClient的调用。

错误原因

  1. Mock对象初始化顺序颠倒:setUp方法中先调用了builder.baseUrl(),但此时MockitoAnnotations.openMocks(this)尚未执行,所有通过@Mock标注的对象还都是null,直接调用方法必然触发NPE。
  2. Builder链式调用未Stub:Service构造函数中依赖builder.baseUrl(...).build()的链式调用创建WebClient,但测试中未对Builder的baseUrl和build方法进行Stub,导致无法生成预期的WebClient实例。
  3. URI路径不匹配:Service中调用的是uri("/employees"),但测试Stub时写的是uri("/all-employees"),两者不一致会导致Stub无法匹配,后续调用会返回默认值(null)。

修复方案

1. 调整Mock初始化顺序

确保在使用任何Mock对象之前,先执行MockitoAnnotations.openMocks(this)完成Mock对象的初始化。

2. Stub Builder的链式调用

需要让Builder的baseUrl方法返回自身(保持链式调用),build方法返回我们Mock的WebClient实例,这样Service构造出来的webClientForEmployee就是我们可控的Mock对象。

3. 对齐URI路径

将测试中Stub的URI路径修改为和Service中一致的"/employees"。

修改后的测试类代码

@ExtendWith(MockitoExtension.class)
class ServiceWithConstructorTest {

    @Mock
    WebClient webClientForEmployee;
    @Mock
    WebClient.Builder builder;
    @Mock
    WebClient.RequestHeadersUriSpec requestHeadersUriSpecMock;
    @Mock
    WebClient.RequestHeadersSpec requestHeadersSpecMock;
    @Mock
    WebClient.ResponseSpec responseSpecMock;

    ServiceWithConstructor serviceWithConstructor;

    @BeforeEach
    public void setUp() {
        // 先初始化所有Mock对象
        MockitoAnnotations.openMocks(this);
        
        // Stub Builder的链式调用:baseUrl返回自身,build返回Mock的WebClient
        when(builder.baseUrl(anyString())).thenReturn(builder);
        when(builder.build()).thenReturn(webClientForEmployee);
        
        // 创建Service实例
        serviceWithConstructor = new ServiceWithConstructor(builder);
    }

    @Test
    void getEmployeesShouldReturnList() {
        // 对齐URI路径为Service中使用的"/employees"
        when(webClientForEmployee.get()).thenReturn(requestHeadersUriSpecMock);
        when(requestHeadersUriSpecMock.uri("/employees")).thenReturn(requestHeadersSpecMock);
        when(requestHeadersSpecMock.retrieve()).thenReturn(responseSpecMock);
        when(responseSpecMock.bodyToMono(Employee[].class)).thenReturn(Mono.just(employeeArrayResponseMock()));

        ResponseEntity<Employee[]> actual = serviceWithConstructor.getEmployees();
        Assertions.assertEquals(HttpStatus.OK.is2xxSuccessful(), actual.getStatusCode().is2xxSuccessful());
    }
    
    // 补充employeeArrayResponseMock方法的实现(示例)
    private Employee[] employeeArrayResponseMock() {
        return new Employee[]{new Employee(1, "Test")};
    }
}

额外说明

  • 使用anyString()匹配baseUrl的参数,可以兼容Service构造函数中传入的任意baseUrl值;如果需要精确匹配,也可以换成具体的字符串参数。
  • 因为已经通过Builder的build方法返回了Mock的webClientForEmployee,Service中使用的WebClient就是我们Mock的实例,后续的Stub逻辑就能正确生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 02:56:03