WebClient Rest调用中Builder抛出空指针异常(NPE)求助
解决WebClient.Builder Mock时的空指针异常(NPE)
问题背景
在为通过WebClient.Builder构造WebClient的Service类编写测试时,已用Mockito Mock了WebClient.Builder,但构造Service实例时抛出空指针异常,提示builder为null,同时后续测试逻辑也无法正确Stub WebClient的调用。
错误原因
- Mock对象初始化顺序颠倒:
setUp方法中先调用了builder.baseUrl(),但此时MockitoAnnotations.openMocks(this)尚未执行,所有通过@Mock标注的对象还都是null,直接调用方法必然触发NPE。 - Builder链式调用未Stub:Service构造函数中依赖
builder.baseUrl(...).build()的链式调用创建WebClient,但测试中未对Builder的baseUrl和build方法进行Stub,导致无法生成预期的WebClient实例。 - 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
相关产品推荐
相关产品推荐

