@WithUserDetails 失败,因为找不到用户名
我正在尝试对我的 AuthController.java 进行测试,但它一直失败,提示找不到用户名。我已将这个问题缩小到 UserService.java 中检查是否 foundUser == null
的行。
这是我的 AuthControllerTests.java
package dev.tdwl.controller;
import dev.tdwl.repository.CategoryListsRepository;
import dev.tdwl.repository.UserRepository;
import dev.tdwl.security.jwt.JwtUtils;
import dev.tdwl.services.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(classes = {AuthController.class, UserService.class})
@AutoConfigureMockMvc
public class AuthControllerTests {
@Autowired
private MockMvc mockMvc;
@MockBean
private PasswordEncoder encoder;
@MockBean
private JwtUtils jwtUtils;
@MockBean
private AuthenticationManager authenticationManager;
@MockBean
private CategoryListsRepository categoryRepo;
@MockBean
private UserRepository userRepository;
@Test
@WithUserDetails(value = "testuser", userDetailsServiceBeanName = "userService")
void testAuthenticationCheckValid() {
try {
mockMvc.perform(get("/auth/check")).andExpect(status().isOk()).andDo(print());
} catch (Exception e) {
e.printStackTrace();
}
}
}
和我的 AuthController.java
package dev.tdwl.controller;
import com.mongodb.MongoException;
import dev.tdwl.model.AuthenticationRequest;
import dev.tdwl.model.CategoryLists;
import dev.tdwl.model.JwtResponse;
import dev.tdwl.model.User;
import dev.tdwl.repository.CategoryListsRepository;
import dev.tdwl.repository.UserRepository;
import dev.tdwl.security.jwt.JwtUtils;
import dev.tdwl.services.UserDetailsImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
@RestController
public class AuthController {
private CategoryListsRepository categoryRepo;
private UserRepository userRepo;
private AuthenticationManager authenticationManager;
private PasswordEncoder encoder;
private JwtUtils jwtUtils;
@Autowired
public AuthController(UserRepository repository, AuthenticationManager authenticationManager, PasswordEncoder encoder, JwtUtils jwtUtils, CategoryListsRepository categoryRepo) {
this.authenticationManager = authenticationManager;
this.userRepo = repository;
this.encoder = encoder;
this.jwtUtils = jwtUtils;
this.categoryRepo = categoryRepo;
}
@GetMapping("/auth/check")
public ResponseEntity<?> verifyLogin() {
UserDetailsImpl userDetails = (UserDetailsImpl) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (userDetails.getId() == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return ResponseEntity.ok(userDetails);
}
@PostMapping("/auth/login")
public ResponseEntity<?> login(@RequestBody AuthenticationRequest authenticationRequest) {
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authenticationRequest.getEmail(), authenticationRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication);
UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
return ResponseEntity.ok(new JwtResponse(jwt, user.getUsername(), user.getId()));
}
@PostMapping("/auth/signup")
public ResponseEntity<?> authenticateClient(@RequestBody AuthenticationRequest authenticationRequest) {
String email = authenticationRequest.getEmail();
String password = authenticationRequest.getPassword();
User newUser = new User(email, encoder.encode(password));
try {
userRepo.save(newUser);
CategoryLists newList = new CategoryLists(newUser.getId(), Collections.emptyList());
categoryRepo.save(newList);
} catch (DuplicateKeyException | MongoException e) {
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
return ResponseEntity.ok("User registered successfully!");
}
}
和 UserService.java
package dev.tdwl.services;
import dev.tdwl.model.User;
import dev.tdwl.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User foundUser = userRepository.findUserByEmail(username);
if (foundUser == null) throw new UsernameNotFoundException("User not found.");
return UserDetailsImpl.build(foundUser);
}
}
失败的行位于 UserService 中。 java 尝试findUserByEmail,因为userRepository被模拟,它只返回null并抛出用户名未找到异常。如果我删除 Mock,那么我的测试会失败,因为它无法在 AuthController 中找到构造函数所需的 UserRepository bean
有关如何正确执行此操作的任何想法吗?
I am trying to make a test for my AuthController.java
but it keeps failing saying that the username is not found. I have narrowed this problem down to the line in UserService.java
that checks if foundUser == null
.
Here is my AuthControllerTests.java
package dev.tdwl.controller;
import dev.tdwl.repository.CategoryListsRepository;
import dev.tdwl.repository.UserRepository;
import dev.tdwl.security.jwt.JwtUtils;
import dev.tdwl.services.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(classes = {AuthController.class, UserService.class})
@AutoConfigureMockMvc
public class AuthControllerTests {
@Autowired
private MockMvc mockMvc;
@MockBean
private PasswordEncoder encoder;
@MockBean
private JwtUtils jwtUtils;
@MockBean
private AuthenticationManager authenticationManager;
@MockBean
private CategoryListsRepository categoryRepo;
@MockBean
private UserRepository userRepository;
@Test
@WithUserDetails(value = "testuser", userDetailsServiceBeanName = "userService")
void testAuthenticationCheckValid() {
try {
mockMvc.perform(get("/auth/check")).andExpect(status().isOk()).andDo(print());
} catch (Exception e) {
e.printStackTrace();
}
}
}
And my AuthController.java
package dev.tdwl.controller;
import com.mongodb.MongoException;
import dev.tdwl.model.AuthenticationRequest;
import dev.tdwl.model.CategoryLists;
import dev.tdwl.model.JwtResponse;
import dev.tdwl.model.User;
import dev.tdwl.repository.CategoryListsRepository;
import dev.tdwl.repository.UserRepository;
import dev.tdwl.security.jwt.JwtUtils;
import dev.tdwl.services.UserDetailsImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
@RestController
public class AuthController {
private CategoryListsRepository categoryRepo;
private UserRepository userRepo;
private AuthenticationManager authenticationManager;
private PasswordEncoder encoder;
private JwtUtils jwtUtils;
@Autowired
public AuthController(UserRepository repository, AuthenticationManager authenticationManager, PasswordEncoder encoder, JwtUtils jwtUtils, CategoryListsRepository categoryRepo) {
this.authenticationManager = authenticationManager;
this.userRepo = repository;
this.encoder = encoder;
this.jwtUtils = jwtUtils;
this.categoryRepo = categoryRepo;
}
@GetMapping("/auth/check")
public ResponseEntity<?> verifyLogin() {
UserDetailsImpl userDetails = (UserDetailsImpl) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (userDetails.getId() == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return ResponseEntity.ok(userDetails);
}
@PostMapping("/auth/login")
public ResponseEntity<?> login(@RequestBody AuthenticationRequest authenticationRequest) {
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authenticationRequest.getEmail(), authenticationRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication);
UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
return ResponseEntity.ok(new JwtResponse(jwt, user.getUsername(), user.getId()));
}
@PostMapping("/auth/signup")
public ResponseEntity<?> authenticateClient(@RequestBody AuthenticationRequest authenticationRequest) {
String email = authenticationRequest.getEmail();
String password = authenticationRequest.getPassword();
User newUser = new User(email, encoder.encode(password));
try {
userRepo.save(newUser);
CategoryLists newList = new CategoryLists(newUser.getId(), Collections.emptyList());
categoryRepo.save(newList);
} catch (DuplicateKeyException | MongoException e) {
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
return ResponseEntity.ok("User registered successfully!");
}
}
and my UserService.java
package dev.tdwl.services;
import dev.tdwl.model.User;
import dev.tdwl.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User foundUser = userRepository.findUserByEmail(username);
if (foundUser == null) throw new UsernameNotFoundException("User not found.");
return UserDetailsImpl.build(foundUser);
}
}
The line this is failing on is in UserService.java
where it tries to findUserByEmail, because userRepository is mocked it just returns null and throws the username not found exception. If I remove the Mock, then my test fails because it couldn't find the UserRepository bean required for the constructor in AuthController
Any ideas on how to do this correctly?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
只需在这一行中为 UserRepository 声明 Mockbean:
它不会工作。您需要在
mockMvc.perform()
之前添加以下代码。这将模拟 findUserByEmail 方法,并且不会返回 null。
just by declaring Mockbean for UserRepository in this line:
It won't work. You need to add following piece of code before
mockMvc.perform()
.This will mock the findUserByEmail method and it will not return null.