Issue
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?
Solution
just by declaring Mockbean for UserRepository in this line:
@MockBean
private UserRepository userRepository;
It won't work. You need to add following piece of code before mockMvc.perform()
.
Mockito.when(userRepository.findUserByEmail(Mockito.anyString)).thenReturn(new User());
This will mock the findUserByEmail method and it will not return null.
Answered By - Jayesh Choudhary
Answer Checked By - Cary Denson (JavaFixing Admin)