如何用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 } }
问题原因
- 链式打桩逻辑错误:Spock中未被显式模拟的方法默认返回null,
restTemplate.getForEntity(...)返回null后,调用getBody()自然触发空指针。 - 方法调用错误:业务代码中
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
相关产品推荐
相关产品推荐

