SpringBoot Mock WebClient时header返回空致空指针异常
解决WebClient Mock时的空指针异常
我们创建了SpringBoot应用,使用WebClient同步调用外部API,采用JUnit 5编写测试用例并通过Mockito Mock WebClient,但运行测试时出现如下空指针异常:
java.lang.NullPointerException: Cannot invoke "org.springframework.web.reactive.function.client.WebClient$RequestHeadersSpec.retrieve()" because the return value of "org.springframework.web.reactive.function.client.WebClient$RequestHeadersSpec.header(String, String[])" is null
测试用例代码
@SpringBootTest @ActiveProfiles("test") public class StrategyTest { @Mock private WebClient.RequestHeadersUriSpec<?> requestHeadersUriMock; @Mock private WebClient.RequestHeadersSpec<?> requestHeadersMock; @Mock private WebClient.ResponseSpec responseMock; @MockBean @Qualifier("kwebclient") private WebClient kwebclient; @MockBean private AuthTokenManager authTokenManager; @MockBean private TokenRefreshScheduler tokenRefreshScheduler; @Test void testOptimise1() { when(authTokenManager.getAuthToken()).thenReturn(authTokenResponse); prepareWebClientMock(); CReady cReady = setTestCReady(); Strategy.invokeGetScItem(LocationAPIUrl, lin, kwebclient, authTokenManager, tokenRefreshScheduler); assertNotNull(cReady); } private void prepareWebClientMock() { doReturn(requestHeadersUriMock).when(kongWebclient).get(); lenient().when(requestHeadersUriMock.uri(Mockito.any(Function.class))).thenReturn(requestHeadersMock); doReturn(requestHeadersMock).when(requestHeadersMock).accept(notNull()); doReturn(requestHeadersMock).when(requestHeadersMock).header(notNull(), notNull()); doReturn(responseMock).when(requestHeadersMock).retrieve(); doReturn(Mono.just(APIResponseJsonString)).when(responseMock).toEntity(String.class); } }
业务代码中API调用逻辑
Mono<ResponseEntity<String>> monoresponse = kWebclient.get() .uri(uriBuilder -> uriBuilder.path(finalurl).build()) .header("Authorization", "Bearer " + authTokenManager.getAuthToken().getAccessToken()).retrieve() .toEntity(String.class); response = monoresponse.block();
WebClient创建代码
@Bean @Qualifier("kWebclient") public WebClient createWebClient() throws Exception { return buildSslEnabledWebClient(); } private WebClient buildSslEnabledWebClient() throws Exception { final KeyStore trustStore = createKeyStore(trustStorePath, kConfigVault.getKTrustStorePassword()); try { final TrustManagerFactory trustManager = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManager.init(trustStore); final SslContext sslContext = SslContextBuilder.forClient().trustManager(trustManager).build(); final HttpClient httpClient = HttpClient.create().secure(ssl -> { ssl.sslContext(sslContext); }); final int size = 16 * 1024 * 1024; final ExchangeStrategies strategies = ExchangeStrategies.builder() .codecs(codecs -> codecs.defaultCodecs().maxInMemorySize(size)).build(); return WebClient.builder().exchangeStrategies(strategies) .clientConnector(new ReactorClientHttpConnector(httpClient)).build(); } catch (NoSuchAlgorithmException | KeyStoreException | SSLException e) { log.error("Could not initialize Webclient with the trustore data", e); throw e; } }
问题原因与修复方案
问题出在两个地方:
- 方法重载匹配错误:业务代码调用的是
header(String, String)(单个字符串参数的重载),但测试中mock的header(notNull(), notNull())匹配的是header(String, String[])(数组参数的重载),导致实际调用时没有匹配到mock逻辑,返回null,触发空指针。 - 变量名笔误:测试中
when(kongWebclient).get()里的kongWebclient是笔误,应该是kwebclient,否则get()方法的mock不会生效。
修正后的prepareWebClientMock方法
private void prepareWebClientMock() { // 修正变量名笔误 doReturn(requestHeadersUriMock).when(kwebclient).get(); lenient().when(requestHeadersUriMock.uri(Mockito.any(Function.class))).thenReturn(requestHeadersMock); doReturn(requestHeadersMock).when(requestHeadersMock).accept(notNull()); // 匹配单个字符串参数的header重载 doReturn(requestHeadersMock).when(requestHeadersMock).header(notNull(), anyString()); doReturn(responseMock).when(requestHeadersMock).retrieve(); doReturn(Mono.just(APIResponseJsonString)).when(responseMock).toEntity(String.class); }
内容的提问来源于stack exchange,提问作者Amulya M
相关产品推荐
相关产品推荐

