自定义WebClient的Spock测试中GET请求抛出意外空指针异常
问题原因分析
你的测试抛出NPE是因为**responseSpec.onStatus()的Mock配置错误**:
- 原代码中
onStatus()方法接收两个参数(状态判断Predicate和异常处理Function),且返回的是ResponseSpec对象,用于后续链式调用toBodilessEntity()。 - 你当前的Mock
1 * responseSpec.onStatus() >> Mono.error(...)既没有匹配方法参数,还错误地返回了Mono类型,导致后续调用toBodilessEntity()时,对象实际是Mono而非ResponseSpec,触发空指针异常。
修正后的测试用例
class TransitApiClientSpec extends Specification { private WebClient transitApiWebClient private TransitApiClient transitApiClient private OAuth2CustomClient oAuth2CustomClient private TransitApiClientProperties transitApiClientProperties private RequestHeadersSpec requestHeadersSpec private ResponseSpec responseSpec private RequestHeadersUriSpec requestHeadersUriSpec def setup() { transitApiClientProperties = new TransitApiClientProperties() transitApiClientProperties.setServiceUrl("https://test-url") transitApiClientProperties.setFindOfficeOfTransit("/transit?") transitApiClientProperties.setUsername("username") transitApiClientProperties.setPassword("password") transitApiClientProperties.setAuthorizationGrantType("grantType") transitApiClientProperties.setClientId("clientId") transitApiClientProperties.setClientSecret("clientSecret") oAuth2CustomClient = Stub(OAuth2CustomClient) oAuth2CustomClient.getJwtToken() >> "token" transitApiWebClient = Mock(WebClient) requestHeadersSpec = Mock(RequestHeadersSpec) responseSpec = Mock(ResponseSpec) requestHeadersUriSpec = Mock(RequestHeadersUriSpec) transitApiClient = new TransitApiClient(transitApiClientProperties, transitApiWebClient, oAuth2CustomClient) } def "request validation of OoTra and throw InvalidOfficeException"(){ given : def officeId = "testId" def uri = UriComponentsBuilder .fromUriString(transitApiClientProperties.getFindOfficeOfTransit()) .queryParam("codelistKey", "CL173") .queryParam("itemCode", officeId) .build() .toUriString() def expectedException = new InvalidOfficeException(null, "Invalid Office exception occurred while invoking :/transit?testId") 1 * transitApiWebClient.get() >> requestHeadersUriSpec 1 * requestHeadersUriSpec.uri(uri) >> requestHeadersSpec 1 * requestHeadersSpec.header(HttpHeaders.AUTHORIZATION, "Bearer token") >> requestHeadersSpec 1 * requestHeadersSpec.retrieve() >> responseSpec // 正确匹配onStatus的参数,并返回ResponseSpec以维持链式调用 1 * responseSpec.onStatus( { it.value() == HttpStatus.NO_CONTENT.value() }, _ as Function<ClientResponse, Mono<? extends Throwable>> ) >> responseSpec // 模拟toBodilessEntity抛出预期异常 1 * responseSpec.toBodilessEntity() >> Mono.error(expectedException) when : transitApiClient.isOfficeOfTransitValidAndNational(officeId) then : thrown(InvalidOfficeException) } }
关键修正点
- 匹配
onStatus的参数:使用Spock的参数匹配器,第一个参数匹配204状态判断逻辑,第二个参数匹配任意异常处理Function。 onStatus返回正确类型:让onStatus返回responseSpec本身,保证链式调用的完整性。- 模拟
toBodilessEntity抛出异常:因为原代码中onStatus的逻辑是当状态为204时,在toBodilessEntity()执行时触发异常,所以直接MocktoBodilessEntity()返回Mono.error(),这样调用block()时就会抛出预期的InvalidOfficeException。
内容的提问来源于stack exchange,提问作者JavaEnthusiast27
相关产品推荐
相关产品推荐

