Spring Boot JUnit集成测试中注册接口404报错的修复方法
在Spring Boot项目中编写JUnit集成测试,测试AuthController的注册接口时,运行shouldRegisterForAdmin方法出现404 User Name testadmin not found error错误,错误发生在mock AuthenticationManager的返回语句处。
AuthController注册方法
public ResponseEntity<AuthResponse> register(@RequestBody RegisterRequest registerRequest) { var registeredUser = authService.register(registerRequest); UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(registeredUser.getUsername(), registerRequest.getPassword()); Authentication auth = authenticationManager.authenticate(authToken); SecurityContextHolder.getContext().setAuthentication(auth); String jwtToken = jwtTokenProvider.generateJwtToken(auth); AuthResponse authResponse = AuthResponse.builder() .accessToken(jwtToken) .message("success") .build(); return new ResponseEntity<>(authResponse, HttpStatus.CREATED); }
RegisterRequest构造方法
public RegisterRequest getRegisterRequest(){ return RegisterRequest.builder() .username("testadmin") .password("testadmin") .countryCode("1") .lineNumber("1234567890") .firstName("First Name Admin") .lastName("Last Name Admin") .email("testadmin@afet.com") .organizationId(1L) .build(); }
测试类代码
@SpringBootTest @ExtendWith(MockitoExtension.class) @AutoConfigureMockMvc(addFilters = false) class AuthControllerTest { private final String ADMIN_CONTROLLER_BASEURL = "/api/v1/admin"; @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; @MockBean private AuthService authService; @Mock private PasswordEncoder passwordEncoder; @Mock private JwtTokenProvider jwtTokenProvider; @Mock private AuthenticationManager authenticationManager; @Mock private SecurityContext securityContext; @Test void shouldRegisterForAdmin() throws Exception { // Given RegisterRequest registerRequest = new UserBuilder().getRegisterRequest(); User user = new UserBuilder() .withRegisterRequest(registerRequest,passwordEncoder).build(); UserDTO userDto = UserDTO.builder() .username(user.getUsername()) .firstName(user.getFirstName()) .lastName(user.getLastName()) .userRole(user.getUserRole()) .countryCode(user.getCountryCode()) .lineNumber(user.getLineNumber()) .userStatus(user.getStatus()) .email(user.getEmail()) .lastLoginDate(user.getLastLoginDate()) .build(); AuthResponse authResponse = new AuthResponseBuilder() .withMessage("success") .withAccessToken("test_token") .build(); // when when(authService.register(registerRequest)).thenReturn(userDto); when(authenticationManager.authenticate(any(UsernamePasswordAuthenticationToken.class))) .thenReturn(new UsernamePasswordAuthenticationToken(userDto.getUsername(),registerRequest.getPassword())); // ERROR LINE when(jwtTokenProvider.generateJwtToken(any(Authentication.class))).thenReturn("test_token"); SecurityContextHolder.setContext(securityContext); // then - Perform the POST request and assert the response mockMvc.perform(post(ADMIN_CONTROLLER_BASEURL + "/register") .contentType("application/json") .content(objectMapper.writeValueAsString(registerRequest))) .andDo(print()) .andExpect(status().isCreated()) .andExpect(jsonPath("$.accessToken").value(authResponse.getAccessToken())) .andExpect(jsonPath("$.message").value(authResponse.getMessage())); } }
错误行
when(authenticationManager.authenticate(any(UsernamePasswordAuthenticationToken.class))) .thenReturn(new UsernamePasswordAuthenticationToken(userDto.getUsername(),registerRequest.getPassword()));
修正AuthenticationManager的mock方式:测试类中
AuthenticationManager使用@Mock只会创建普通mock对象,不会替换Spring容器中的真实实例。AuthController注入的真实AuthenticationManager会尝试去数据库查找testadmin(但该用户仅通过mock的AuthService返回,并未实际入库),因此触发"用户未找到"错误。将@Mock改为@MockBean,让Spring容器使用mock实例替代真实实现:@MockBean private AuthenticationManager authenticationManager;返回已认证的Authentication对象:当前mock返回的
UsernamePasswordAuthenticationToken是通过(principal, credentials)构造器创建的,默认处于未认证状态,后续逻辑(如JwtTokenProvider生成token)可能需要已认证对象。推荐使用带权限参数的构造器创建已认证token:when(authenticationManager.authenticate(any(UsernamePasswordAuthenticationToken.class))) .thenReturn(new UsernamePasswordAuthenticationToken( userDto.getUsername(), registerRequest.getPassword(), Collections.singletonList(new SimpleGrantedAuthority("ROLE_ADMIN")) // 根据实际角色调整 ));也可以手动设置认证状态:
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDto.getUsername(), registerRequest.getPassword()); authToken.setAuthenticated(true); when(authenticationManager.authenticate(any(UsernamePasswordAuthenticationToken.class))) .thenReturn(authToken);测试后清理SecurityContext(可选):避免当前测试的SecurityContext状态影响其他测试:
@AfterEach void tearDown() { SecurityContextHolder.clearContext(); }
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu

