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

Spring Boot+Mockito:Mock RestTemplate时返回值始终为空的问题

Mockito测试RestTemplate返回空指针的问题解决

问题诊断

  1. RestTemplate未被正确Mock:服务类里的RestTemplate是通过new RestTemplate(...)直接初始化的,不是依赖注入的实例,导致@InjectMocks无法替换掉原有的RestTemplate。测试时服务类还是用自己创建的真实RestTemplate,而非Mock对象,所以when(...).thenReturn(...)完全不生效,postForObject返回null触发空指针。
  2. postForObject参数匹配错误:原方法调用的是restTemplate.postForObject(String, MultiValueMap, Class),但测试中第二个参数匹配的是any(Class.class),和实际传入的MultiValueMap类型不匹配,即使RestTemplate被正确Mock,这个匹配也不会触发。
  3. CaptchaResponse的Mock设置错误:用mock(CaptchaResponse.class)创建的是Mock对象,直接调用setSuccess(false)不会改变其内部状态(Mock对象的方法默认无实际逻辑),需要用Mockito的Stubbing指定getSuccess()的返回值。

解决方案

步骤1:让RestTemplate可被Mock替换

推荐把服务类的RestTemplate改成构造注入,这样测试时@InjectMocks能自动替换成Mock实例:

@Service
public class CaptchaValidatorServiceImpl implements CaptchaValidatorService{
   private static final String GOOGLE_CAPTCHA_ENDPOINT = "someEndpoint";
   private String stage;
   private String captchaSecret;
   private final RestTemplate restTemplate;

   // 构造注入RestTemplate
   public CaptchaValidatorServiceImpl() {
       // 保留原有初始化逻辑,或者改为注入外部Bean
       this.restTemplate = new RestTemplate(getClientHttpRequestFactory());
   }

   // 或者如果用外部配置的RestTemplate Bean:
   // public CaptchaValidatorServiceImpl(RestTemplate restTemplate) {
   //     this.restTemplate = restTemplate;
   // }

   @Override
   public void checkToken(String token) throws Exception{
      // 原有逻辑不变
   }
   private SimpleClientHttpRequestFactory getClientHttpRequestFactory(){
      // 原有实现
   }
}

如果不想修改服务类构造,也可以用反射替换Spy对象里的RestTemplate字段,在测试类中添加:

@BeforeEach
void setUp() throws NoSuchFieldException, IllegalAccessException {
    Field restTemplateField = CaptchaValidatorServiceImpl.class.getDeclaredField("restTemplate");
    restTemplateField.setAccessible(true);
    restTemplateField.set(captchaValidatorService, restTemplate);
}

步骤2:修正postForObject的参数匹配

测试中参数要和实际调用匹配,第二个参数是MultiValueMap,所以调整匹配规则:

Mockito.when(restTemplate.postForObject(
    Mockito.anyString(),
    ArgumentMatchers.any(MultiValueMap.class),
    ArgumentMatchers.eq(CaptchaResponse.class)
)).thenReturn(captchaResponse);

或者用更宽松的匹配(适合不关心请求体内容的场景):

Mockito.when(restTemplate.postForObject(
    Mockito.anyString(),
    ArgumentMatchers.any(),
    ArgumentMatchers.eq(CaptchaResponse.class)
)).thenReturn(captchaResponse);

步骤3:正确设置CaptchaResponse的返回值

因为是Mock对象,直接调用setSuccess无效,需要指定getSuccess()的返回值:

// 替换原有的captchaResponse.setSuccess(false);
Mockito.when(captchaResponse.getSuccess()).thenReturn(false);

如果CaptchaResponse是简单POJO,也可以直接用真实对象代替Mock:

private CaptchaResponse captchaResponse = new CaptchaResponse();
// 直接设置属性
captchaResponse.setSuccess(false);

完整修正后的测试类

@SpringBootTest
public class CaptchaValidatorTest{
   @Mock
   private RestTemplate restTemplate;
   @InjectMocks
   private CaptchaValidatorServiceImpl captchaValidatorService;
   private CaptchaResponse captchaResponse = mock(CaptchaResponse.class);

   @BeforeEach
   void setUp() throws NoSuchFieldException, IllegalAccessException {
       // 反射替换服务类中的RestTemplate(如果没改构造注入的话)
       Field restTemplateField = CaptchaValidatorServiceImpl.class.getDeclaredField("restTemplate");
       restTemplateField.setAccessible(true);
       restTemplateField.set(captchaValidatorService, restTemplate);
   }

   @Test
   public void shouldThrowInvalidTokenException() {
      // 设置CaptchaResponse的getSuccess返回false
      Mockito.when(captchaResponse.getSuccess()).thenReturn(false);
      // 正确匹配postForObject参数
      Mockito.when(restTemplate.postForObject(
          Mockito.anyString(),
          ArgumentMatchers.any(MultiValueMap.class),
          ArgumentMatchers.eq(CaptchaResponse.class)
      )).thenReturn(captchaResponse);

      Exception exception = assertThrows(InvalidCaptchaTokenException.class, () -> 
                captchaValidatorService.checkToken("test"));
      assertEquals("Invalid Token", exception.getMessage());
   }
} 

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 15:45:34