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

如何用Spock模拟Spring RestTemplate的getForEntity方法解决空指针问题

Spock模拟RestTemplate的getForEntity时getBody()空指针问题

业务代码

Product getProducts(ProductSearch request) {
  Map<String, Object> queryParam = new HashMap<>();
  queryParam.put("id", request.getProductId());
  queryParam.put("category", request.getCategory());

  UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(url);
  queryParam.forEach(uriComponentsBuilder::queryParam);

  String url = uriComponentsBuilder.toUriString();
  return restTemplate.getForEntity(url, Product.class).getBody();
}

问题场景

尝试用Spock模拟RestTemplate的getForEntity方法,但测试代码运行时抛出NullPointerException,测试代码如下:

class ProductClientSpec extends Specification {
  RestTemplate restTemplate = Mock()
  ProductClient legacyOpenSlotsClient = new ProductClient(restTemplate)

  def setup() {}

  def 'should return product'() {
    given:
    ProductSearch request = createProductRequest()
    Product product = createProductResponse()

    when:
    ProductClient.getProducts(request)

    then:
    1 * restTemplate.getForEntity("http://localhost:8080/products", Product.class).getBody() >> product
  }
}

问题原因

  1. 链式打桩逻辑错误:Spock中未被显式模拟的方法默认返回null,restTemplate.getForEntity(...)返回null后,调用getBody()自然触发空指针。
  2. 方法调用错误:业务代码中getProducts是实例方法,但测试里用了静态调用ProductClient.getProducts(request),不符合代码逻辑。

正确解决方案

方案1:Mock ResponseEntity对象

先模拟getForEntity返回一个Mock的ResponseEntity,再对该对象的getBody()方法打桩:

class ProductClientSpec extends Specification {
  RestTemplate restTemplate = Mock()
  ProductClient productClient = new ProductClient(restTemplate)

  def 'should return product correctly'() {
    given:
    ProductSearch request = createProductRequest()
    Product product = createProductResponse()
    ResponseEntity<Product> mockResponseEntity = Mock()

    when:
    Product result = productClient.getProducts(request)

    then:
    // 匹配任意URL,也可根据业务逻辑写具体带参数的URL
    1 * restTemplate.getForEntity(_ as String, Product.class) >> mockResponseEntity
    1 * mockResponseEntity.getBody() >> product
    result == product
  }
}

方案2:直接返回真实ResponseEntity实例

如果不需要Mock ResponseEntity的其他方法,直接返回真实的ResponseEntity对象更简洁:

class ProductClientSpec extends Specification {
  RestTemplate restTemplate = Mock()
  ProductClient productClient = new ProductClient(restTemplate)

  def 'should return product correctly'() {
    given:
    ProductSearch request = createProductRequest()
    Product product = createProductResponse()

    when:
    Product result = productClient.getProducts(request)

    then:
    1 * restTemplate.getForEntity(_ as String, Product.class) >> new ResponseEntity<>(product, HttpStatus.OK)
    result == product
  }
}

额外注意事项

  • 必须调用实例的getProducts方法,而非静态调用。
  • 不要写死URL字符串,业务代码会拼接查询参数,可用_匹配任意URL,或用Spock匹配器(如contains("id=123"))匹配具体参数,避免因参数变化导致测试失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 17:52:45