如何为返回RequestBodyUriSpec的RestClient编写Spock测试并解决空指针异常
如何正确Mock Spring RestClient的流畅API进行Spock测试?
你当前的测试出现空指针异常,核心原因是没有正确模拟RestClient流畅API的链式调用逻辑。原测试用通配的交互匹配所有方法,导致链式调用中某个环节返回null,触发空指针。
要解决这个问题,需要按RestClient的调用链顺序,逐个Mock每个方法的返回对象,并最终返回包装好的ResponseEntity(因为你的方法返回类型是ResponseEntity<ProductCatalog>,而非直接返回ProductCatalog)。
正确的Spock测试代码
package com.scf.client import com.scf.domain.model.ProductCatalog import com.scf.domain.model.ProductSearchRequest import org.springframework.http.ResponseEntity import org.springframework.http.MediaType import org.springframework.web.client.RestClient import spock.lang.Specification class ProductRestClientSpec extends Specification { // 定义链式调用所需的所有Mock组件 RestClient restClient = Mock() RestClient.RequestBodyUriSpec mockRequestBodyUriSpec = Mock() RestClient.RequestBodySpec mockRequestBodySpec = Mock() RestClient.ResponseSpec mockResponseSpec = Mock() ProductRestClient productRestClient def setup() { productRestClient = new ProductRestClient(restClient) } def "Should retrieve a Product Catalog for a valid request"() { given: def request = createProductSearchRequest() def productCatalog = createProductCatalogResponse() // 按方法返回类型包装结果 def expectedResponse = ResponseEntity.ok(productCatalog) when: def result = productRestClient.findProduct(request) then: // 严格按调用链顺序Mock每个步骤的返回值 1 * restClient.post() >> mockRequestBodyUriSpec 1 * mockRequestBodyUriSpec.uri("/products/catalog") >> mockRequestBodySpec 1 * mockRequestBodySpec.contentType(MediaType.APPLICATION_JSON) >> mockRequestBodySpec 1 * mockRequestBodySpec.body(request) >> mockRequestBodySpec 1 * mockRequestBodySpec.retrieve() >> mockResponseSpec 1 * mockResponseSpec.toEntity(ProductCatalog.class) >> expectedResponse // 验证返回结果的正确性 result.statusCode.is2xxSuccessful() result.body == productCatalog } def createProductSearchRequest() { ProductSearchRequest.builder() .catalogId("XF-3333") .inStock(true) .build() } def createProductCatalogResponse() { ProductCatalog.builder() .productDescription("Ralph Lauren Fragrance") .productId("PRD-99W222") .productName("Ralph Cologne") .build() } }
关键说明
- 链式调用Mock:RestClient的流畅API每个方法都会返回不同的Spec对象,必须按调用顺序逐个指定返回的Mock实例,不能用通配匹配所有方法。
- 返回类型匹配:目标方法返回的是
ResponseEntity<ProductCatalog>,因此需要用ResponseEntity.ok()包装ProductCatalog实例,避免类型不匹配。 - 交互验证:通过
1 *确保每个方法都被调用一次,同时验证最终返回的ResponseEntity状态码和body是否符合预期。
内容的提问来源于stack exchange,提问作者BreenDeen
相关产品推荐
相关产品推荐

