Mockito模拟RestTemplate postForEntity时CaptchaResponse.success始终为false
问题分析与解决
核心问题
你的测试mock未生效,控制器实际在调用真实的reCAPTCHA接口而非mock对象,导致success字段始终为false。原因有两点:
- 控制器内直接实例化RestTemplate(
new RestTemplate()),未通过依赖注入获取实例,测试中mock的RestTemplate根本没被用到。 - CaptchaResponse的注解配置可能影响RestTemplate的反序列化逻辑。
解决方案
1. 修改控制器,注入RestTemplate
将RestTemplate改为依赖注入,让Spring能在测试时替换为mock对象:
@RestController @RequiredArgsConstructor public class RegisterController { ... private final String secret = ""; private final String url = "https://www.google.com/recaptcha/api/siteverify"; private final UserService userService; private final VerificationTokenService tokenService; private final MailingService mailingService; // 新增:注入RestTemplate private final RestTemplate restTemplate; ... @PostMapping("/api/auth/register") public ResponseEntity<RestUser> register(@Valid @RequestBody User user, @RequestParam("token") String token) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap<String, String> map = new LinkedMultiValueMap<>(); map.add("secret", secret); map.add("response", token); // 移除:不再手动实例化RestTemplate // RestTemplate restTemplate = new RestTemplate(); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); ResponseEntity<CaptchaResponse> response = restTemplate.postForEntity(url, request, CaptchaResponse.class); ... } ... }
2. 修正测试类的Mock配置
将@Mock改为@MockBean,确保mock对象被Spring注入到控制器中:
@SpringBootTest public class RegisterControllerTest { private MockMvc mock; @Autowired private RegisterController registerController; @Autowired private ObjectMapper objectMapper; @MockBean private UserService userService; @MockBean private VerificationTokenService tokenService; @MockBean private MailingService mailingService; // 修改:@Mock改为@MockBean @MockBean private RestTemplate restTemplate; private static User registeringUser; ... @Test @DisplayName("Test registration of a lambda user for production condition with valid captcha.") public void testRegisterProduction_shouldReturnRestUser_whenCaptchaSuccess() throws Exception { String token = "valid-token"; RestUser expectedRest = new RestUser(registeringUser); VerificationToken expectedToken = new VerificationToken(); CaptchaResponse captchaResponse = new CaptchaResponse(); captchaResponse.setSuccess(true); ResponseEntity<CaptchaResponse> responseEntity = new ResponseEntity<>(captchaResponse, HttpStatus.OK); // 绑定mock行为到注入的restTemplate when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), eq(CaptchaResponse.class))).thenReturn(responseEntity); // 补充mock后续服务方法,避免空指针 when(userService.save(any(User.class))).thenReturn(registeringUser); when(tokenService.save(any(VerificationToken.class))).thenReturn(expectedToken); doNothing().when(mailingService).sendVerificationEmail(any(User.class), anyString()); mock.perform(post("/api/auth/register") .param("token", token) .contentType("application/json") .content(objectMapper.writeValueAsString(registeringUser))) .andExpect(status().isOk()); } }
3. 修复CaptchaResponse的序列化配置
移除无用的@RequiredArgsConstructor,添加无参构造器确保RestTemplate能正确反序列化:
@NoArgsConstructor @Getter @Setter @ToString public class CaptchaResponse { private boolean success; private String challengeTs; private String hostname; private String[] errorcodes; }
为什么这些修改有效
- 控制器通过依赖注入获取RestTemplate后,测试中的
@MockBean才能替换真实实例,mock的postForEntity行为才会生效。 @MockBean会将mock对象注册到Spring上下文,而@Mock仅创建局部mock对象,无法被控制器获取。@NoArgsConstructor确保RestTemplate的消息转换器能通过反射创建CaptchaResponse实例,正确解析响应数据。
内容的提问来源于stack exchange,提问作者newenpoi
相关产品推荐
相关产品推荐

