I have Spring Security app. I added custom SpringDataUserDetailsService implements UserDetailsService
. Autowired 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;
}
}
I have tested by browser and it works ok. I can register new User (to database) and then log in to app. Now I want write test for CustomerController
, but recived error. If I delete Autowired setUserService
test is passed, but i can't register new user. Where I need find problem?
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"));
}
}
CodePudding user response:
You should add
@Import({UserService.class})
to you test class. The Result would be
@ExtendWith(SpringExtension.class)
@WebMvcTest(CustomerController.class)
@Import({UserService.class})
class CustomerControllerTest {
You have annotated your class with "WebMvcTest". In this case spring does not completely starte its entire context. Bean initialization for example is only done for Controller. If you want everything to be initialized (because you are lazy or because your UserService has more dependency that have to be manually imported) you can replace
@WebMvcTest(CustomerController.class)
with
@SpringBootTest
this however will take longer to start your unitTest since it has to initialize more Context