У меня есть приложение Spring Security. Я добавил пользовательский SpringDataUserDetailsService implements UserDetailsService
. Автопроводка setUserService
.
public class SpringDataUserDetailsService implements UserDetailsService {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByEmail(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return new org.springframework.security.core.userdetails.User(
user.getEmail(),
user.getPassword(),
convertAuthorities(user.getRoles()));
}
private Set<GrantedAuthority> convertAuthorities(Set<Role> userRoles) {
Set<GrantedAuthority> authorities = new HashSet<>();
for (Role ur : userRoles) {
authorities.add(new SimpleGrantedAuthority(ur.getRole()));
}
return authorities;
}
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email);
}
@Service
public class UserService {
private static final String DEFAULT_ROLE = "ROLE_USER";
private final UserRepository userRepository;
private final RoleRepository roleRepository;
private final PasswordEncoder passwordEncoder;
public UserService(UserRepository userRepository, RoleRepository roleRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
this.passwordEncoder = passwordEncoder;
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public void registerNewUserAccount(User user) {
if (emailExist(user.getEmail())) {
throw new UserAlreadyExistException("There is an account with that email address: "
+ user.getEmail());
}
// set default role
Role userRole = roleRepository.findRoleByRole(DEFAULT_ROLE);
user.getRoles().add(userRole);
// set hash password
user.setPassword(passwordEncoder.encode(user.getPassword()));
// save
userRepository.save(user);
}
public User findByEmail(String email) {
return userRepository.findByEmail(email);
}
private boolean emailExist(String email) {
return userRepository.findByEmail(email) != null;
}
}
Я проверил через браузер, и он работает нормально. Я могу зарегистрировать нового пользователя (в базу данных), а затем войти в приложение. Теперь я хочу написать тест для CustomerController
, но получил ошибку. Если я удаляю Autowired setUserService
тест пройден, но я не могу зарегистрировать нового пользователя. Где мне нужно найти проблему?
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customUserDetailsService': Unsatisfied dependency expressed through method 'setUserService' parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type '.service.UserService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
@ExtendWith(SpringExtension.class)
@WebMvcTest(CustomerController.class)
class CustomerControllerTest {
@MockBean
private CustomerService customerService;
@Autowired
private MockMvc mockMvc;
@Autowired
private WebApplicationContext context;
@BeforeEach
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
@WithMockUser(value = "spring")
@Test
void shouldReturnViewWithPrefilledData() throws Exception {
Customer customer = new Customer();
customer.setId(1L);
customer.setCustomerName("customer Name");
when(customerService.getAllCustomers()).thenReturn(List.of(customer));
this.mockMvc.perform(MockMvcRequestBuilders.get("/customer/list"))
.andExpect(status().isOk())
.andExpect(view().name("customer/customer-list"))
.andExpect(MockMvcResultMatchers.model().attributeExists("customers"));
}
}
Вы должны добавить
@Import({UserService.class})
вам тестовый класс. Результат будет
@ExtendWith(SpringExtension.class)
@WebMvcTest(CustomerController.class)
@Import({UserService.class})
class CustomerControllerTest {
Вы аннотировали свой класс с помощью «WebMvcTest». В этом случае spring не полностью запускает весь свой контекст. Например, инициализация компонента выполняется только для контроллера. Если вы хотите, чтобы все было инициализировано (потому что вы ленивы или потому что ваш UserService имеет больше зависимостей, которые необходимо импортировать вручную), вы можете заменить
@WebMvcTest(CustomerController.class)
с
@SpringBootTest
это, однако, займет больше времени, чтобы запустить ваш unitTest, так как он должен инициализировать больше контекста