JUnit MockMvc issue? - java

I have an issue with JUnit and MockMvc. Here is my code:
MvcResult result = mockMvcUser.perform(post("/user/adduser")
.content(user)
.contentType(contentType))
.andExpect(status().isOk())
.andReturn();
UserService userService = new UserService();
String userId = result.getResponse().getContentAsString();
User userObj = userService.findUserById(userId);
// the userObj is null and I have no idea why
MvcResult sessionIdObj = mockMvcLogin.perform(post("/login/authenticate")
.param("username", userObj.getEmail())
.param("password", userObj.getPassword())
.contentType(contentType))
.andExpect(status().isOk())
.andReturn();
When I add the user I can see the new entry in the database. When I want to search for it with findUserById I always get null. When I set a breakpoint there I will get the user object every time and the test works as designed. My problem is why do I always get null when I run the test without a breakpoint?
Here is the code of findUserById
public User findUserById(String id) {
Session session = HibernateUtil.getSessionFactory().openSession();
try {
User user = (User) session.get(User.class, id);
return user;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
session.close();
}
}
And I never get an exception. I hope you guys have an idea what's going on there.

Related

authenticate a user inside a test class

I am new to spring boot and testing and I have spring boot app (generated with JHipster) that uses authentication. I need to get the id of the current user.
so this method inside userRepository returns the current user
#Query(value = "select u.* from user u where u.username=?#{principal.username}", nativeQuery = true)
User findConnectedUser();
here is the method I want to test in my controller:
#PostMapping("/rdvs")
public ResponseEntity<Rdv> createRdv(#RequestBody Rdv rdv) throws URISyntaxException {
log.debug("REST request to save Rdv : {}", rdv);
if (rdv.getId() != null) {
throw new BadRequestAlertException("A new rdv cannot already have an ID", ENTITY_NAME, "idexists");
}
User user = userRepository.findConnectedUser();
rdv.setIdUser(user.getId());
Rdv result = rdvRepository.save(rdv);
return ResponseEntity
.created(new URI("/api/rdvs/" + result.getId()))
.headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString()))
.body(result);
}
here is my test method
#Autowired
private RdvRepository rdvRepository;
#Autowired
private UserRepository userRepository;
....
#Test
#Transactional
void createRdv() throws Exception {
int databaseSizeBeforeCreate = rdvRepository.findAll().size();
// Create the Rdv
restRdvMockMvc
.perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(rdv)))
.andExpect(status().isCreated());
// Validate the Rdv in the database
User user = userRepository.findConnectedUser();// this generate the NullPointer exception
List<Rdv> rdvList = rdvRepository.findAll();
assertThat(rdvList).hasSize(databaseSizeBeforeCreate + 1);
Rdv testRdv = rdvList.get(rdvList.size() - 1);
assertThat(testRdv.getDescription()).isEqualTo(DEFAULT_DESCRIPTION);
assertThat(testRdv.getIdUser()).isEqualTo(user.getId());
}
So this method generate a NullPointer, I guess because the method can't find the current user which should be authenticated first. So how can I authenticate a user inside that test method please I spend a lot of time with it but nothing seems to be working
note: I tried to call this api that authenticate users
#PostMapping("/authenticate")
public ResponseEntity<JWTToken> authorize(#Valid #RequestBody LoginVM loginVM) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
loginVM.getUsername(),
loginVM.getPassword()
);
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.createToken(authentication, loginVM.isRememberMe());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);
return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
}
like this in test method
User user = new User();
user.setLogin("user-jwt-controller");
user.setEmail("user-jwt-controller#example.com");
user.setActivated(true);
user.setPassword(passwordEncoder.encode("test"));
userRepository.saveAndFlush(user);
LoginVM loginVM = new LoginVM();
loginVM.setUsername("user-jwt-controller");
loginVM.setPassword("test");
//I don't know how to call the api #PostMapping("/authenticate")
Thanks in advance
Have a look at #WithMockUser annotation, see https://docs.spring.io/spring-security/site/docs/5.0.x/reference/html/test-method.html
You can see an example in the project that was generated by JHipster:
#AutoConfigureMockMvc
#WithMockUser(value = TEST_USER_LOGIN)
#IntegrationTest
class AccountResourceIT {

MockMvc SpringBoot integration tests passing individually but failing when running consecutively? (DataIntegrityViolation)

I have developed integration tests using MockMvc to send requests to the API server.
When I run each test individually they pass, but if I run the entire test class, the first passes and the rest fail due to:
DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["PUBLIC.UK_SB8BBOUER5WAK8VYIIY4PF2BX_INDEX_2 ON PUBLIC.USER(USERNAME) VALUES 4"; SQL statement:
I understand that this means the tests are trying to add another User entity to the repo where one already exists, but I don't understand why the repo is not being reset after each test?
The tests rely on methods to avoid repeated code in each test, and this must be having an effect because only my test classes with these methods are failing like this, but I cannot think why or how to fix it.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class GameControllerTest {
#Autowired
MockMvc mvc;
#Autowired
GameService gameService;
#Autowired
GameRepository gameRepository;
#Autowired
UserRepository userRepository;
#Autowired
UserService userService;
public String username1 = "ss";
public String username2 = "dd";
public String username3 = "ff";
public String location = "London";
public String password = "Password72-";
public String apiVersion = "v1";
public final String gamesUrl = "/api/" + apiVersion + "/games/";
public final String addGameUrl = gamesUrl + "add-game";
public final String gameHistoryUrl = gamesUrl + "your-game-history";
public final String signupUrl = "/api/" + apiVersion + "/users/signup";
public final String loginUrl = "/api/" + apiVersion + "/login";
#Test
#Transactional
public void addGameTest() throws Exception {
String token = signupAndLogin();
Set<User> users = new HashSet<>();
User user = userRepository.findByUsername(username1);
User user2 = userRepository.findByUsername(username2);
users.add(user);
users.add(user2);
Game game = new Game(users, username1, username2, ResultForWhite.LOSE.getUrl(), "1.e4e5", username2);
MvcResult mvcResult = mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game)).header("Authorization", token))
.andExpect(status().isOk()).andReturn();
//Test JSON response is correct.
String jsonResult = mvcResult.getResponse().getContentAsString();
assertThat(jsonResult.contains(username1));
assertThat(jsonResult.contains(username2));
//Test that Game stored in DB correctly.
Optional<Game> savedGame = gameRepository.findById(1L);
assertThat(savedGame.get().getGameId() == 1);
//Test that user victories and defeats have been incremented correctly.
//Should be 1.
User savedUser = userRepository.findByUsername(username1);
User savedUser2 = userRepository.findByUsername(username2);
UserStats savedUserStats = savedUser.getUserStats().get(0);
UserStats savedUserStats2 = savedUser2.getUserStats().get(0);
Integer user1Victories = savedUserStats.getVictories();
Integer user1Defeats = savedUserStats.getDefeats();
Integer user2Victories = savedUserStats2.getVictories();
Integer user2Defeats = savedUserStats2.getDefeats();
//Assert that magnus lost the game and his defeats was incremented, and victories unchanged.
assertThat(user1Victories == 0 && user1Defeats == 1);
//Assert that hikaru won the game and his victories was incremented, and defeats unchanged.
assertThat(user2Victories == 1 && user2Defeats == 0);
}
#Test
#Transactional
public void getAUsersCompleteGameHistory() throws Exception {
String token = signupAndLogin();
createGames(token);
String content = "{\"keyword\":\"null\"}";
MvcResult mvcResult = mvc.perform(post(gameHistoryUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(content).header("Authorization", token))
.andExpect(status().isOk()).andReturn();
String jsonResult = mvcResult.getResponse().getContentAsString();
System.out.println(jsonResult);
//Ensure the JSON object returned contains all the game entities.
assertTrue(jsonResult.contains("{\"gameId\":1") || jsonResult.contains("{\"gameId\":2") || jsonResult.contains("{\"gameId\":3") || jsonResult.contains("{\"gameId\":4") || jsonResult.contains("{\"gameId\":5") || jsonResult.contains("{\"gameId\":6"));
}
public String signupAndLogin() throws Exception {
User user = new User(1L, username1, password, location);
mvc.perform(post(signupUrl)
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(user)))
.andExpect(status().isOk());
Optional<User> savedUser = userService.getUser(1L);
System.out.println(savedUser.toString());
//2nd User
User user2 = new User(2L, username2, password, location);
mvc.perform(post(signupUrl)
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(user2)))
.andExpect(status().isOk());
Optional<User> savedUser2 = userService.getUser(2L);
System.out.println(savedUser2.toString());
//3rd User
User user3 = new User(3L, username3, password, "Bristol");
mvc.perform(post(signupUrl)
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(user3)))
.andExpect(status().isOk());
Optional<User> savedUser3 = userService.getUser(3L);
System.out.println(savedUser3.toString());
//LOGIN
MvcResult mvcResult = mvc.perform(post(loginUrl)
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(user)))
.andExpect(status().isOk())
.andReturn();
String token = mvcResult.getResponse().getHeader("Authorization");
System.out.println("Bearer Token: " + token);
return token;
}
#Transactional
public void createGames(String token) throws Exception {
Set<User> users = new HashSet<>();
Set<User> users2 = new HashSet<>();
User user = userRepository.findByUsername(username1);
User user2 = userRepository.findByUsername(username2);
User user3 = userRepository.findByUsername(username3);
System.out.println("ss: " + username1 + " dd " + username2 + " ff: " + username3);
//Add the User objects
users.add(user);
users.add(user2);
//Add the User objects
users2.add(user);
users2.add(user3);
System.out.println(users2.toString());
//Create 4 new Game objec
Game game = new Game(users, username1, username2username2);
Game game2 = new Game(users, username1, username2, username2);
Game game3 = new Game(users, username2, username1, username2);
Game game4 = new Game(users, username2, username1,username2);
//Create 2 new Game objects
Game game5 = new Game(users2, username3, username1, username3);
Game game6 = new Game(users2, username1, username3, username3);
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game)).header("Authorization", token))
.andExpect(status().isOk());
System.out.println("DEFEATS TEST : //////// :" + userRepository.findByUsername(username2).getUserStats().get(0).getDefeats());
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game2)).header("Authorization", token))
.andExpect(status().isOk());
System.out.println(userRepository.findByUsername(username2).getUserStats().get(0).getVictoryScore());
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game3)).header("Authorization", token))
.andExpect(status().isOk());
System.out.println(userRepository.findByUsername(username2).getUserStats().get(0).getVictoryScore());
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game4)).header("Authorization", token))
.andExpect(status().isOk());
System.out.println(userRepository.findByUsername(username2).getUserStats().get(0).getVictoryScore());
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game5)).header("Authorization", token))
.andExpect(status().isOk());
mvc.perform(post(addGameUrl)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(asJsonString(game6)).header("Authorization", token))
.andExpect(status().isOk());
}
I am not sure but perchance if I refactored this to use #BeforeEach it would work? I just don't know if it will work though.
I have looked into #BeforeEach and I am unsure how I could get the token from the signUp method call e.g:
#BeforeEach
void setup( WebApplicationContext wac) throws Exception {
this.mvc = MockMvcBuilders.webAppContextSetup( wac )
.apply(SecurityMockMvcConfigurers.springSecurity())
.alwaysDo( signupAndLogin() )
.build();
}
Thanks
Edit:
I have added this to the 2nd test:
#Test
public void getAUsersCompleteGameHistory() throws Exception {
userRepository.deleteAll();
gameRepository.deleteAll();
//This doesn't print anything.
for (User u: userRepository.findAll()
) {
System.out.println();
System.out.println(u.getUsername());
System.out.println();
}
//This passes, user Repo.findAll IS empty
assertTrue(userRepository.findAll().isEmpty());
assertTrue(gameRepository.findAll().isEmpty());
String token = signupAndLogin();
createGames(token);
ETC...
}
So the repo are empty, it must be something in signUpAndLogin?
You can put #Transactional on your test class, instead of every test case, if all of the test require a rollback. I am not sure why it doesn't work in your case, so i will offer you some workarounds.
You can do a cleanup after every test:
#AfterEach
public void cleanup() {
gameRepository.deleteAll();
userRepository.deleteAll();
}
That is assuming that your repositories do not contain previous data required to run tests correctly. Or create custom method to delete by username in your repo, to delete only users created for tests, for example:
userRepository.deleteByUsername(username);
In order to initialize the token in BeforeEach method, and then access it, you need to keep it in an instance variable:
#BeforeEach
public void setup() throws Exception {
//instance variable
this.token = signupAndLogin();
}
But that will also lead to constraint violation if the DB is not cleared. If it's the username constraint which causes the problem, you can somewhat hack it by assigning random values to usernames in tests - UUID.randomUUID().toString() for example.
As a side note, i believe assertions using assertThat should look like that:
assertThat(user2Victories == 1 && user2Defeats == 0).isEqualTo(true);
assertThat(user2Victories == 1 && user2Defeats == 0); actually does not assert anything.

Test result is not as expected

Hey I have service that throws exception when user is not found, but when I am trying to test method it doesnt work as expected, it works when I test in postman and the message is right with 400 error, but test doesnt seem to work..
#Test
#WithMockUser(username = "admin", authorities = {"ADMIN"})
public void shoulThrowExceptionGettingUser() throws Exception {
doThrow(new ResponseStatusException(HttpStatus.NOT_FOUND))
.when(userService).getUser("TEST");
MvcResult s = mvc.perform(get("/api/user")
.contentType(MediaType.APPLICATION_JSON)
.param("username","Test")).andDo(print())
.andExpect(status().isBadRequest()).andReturn();
}
public UserDtoNoPass getUser(String userName)
{
try {
UserDetails userDetails= manager.loadUserByUsername(userName);
UserDtoNoPass toReturn= new UserDtoNoPass();
toReturn.setUserName(userDetails.getUsername());
toReturn.setList(userDetails.getAuthorities().stream().map(s->s.toString()).map(
s->Enum.valueOf(Authority.class,s)).collect(Collectors.toList()));
return toReturn;
}
catch (UsernameNotFoundException e)
{
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
}
}
#GetMapping
public ResponseEntity<Object> getUser(#RequestParam("username") String username)
{
HttpHeaders headers= new HttpHeaders();
try{
UserDtoNoPass dto=userService.getUser(username);
headers.add("successfull","true");
return new ResponseEntity<>(dto,headers, HttpStatus.OK);
}
catch (Exception e)
{
headers.add("successfull","false");
return new ResponseEntity<>("No such a user found",headers, HttpStatus.BAD_REQUEST);
}
}
The mock is set up for a String with value "TEST":
doThrow(new ResponseStatusException(HttpStatus.NOT_FOUND))
.when(userService).getUser("TEST");
but the actual call uses a String with value "Test":
MvcResult s = mvc.perform(get("/api/user")
...
.param("username","Test")
...
Both Strings should have the same value. I personally like to keep mock setup and mock validation separate. Thus I would advise to rewrite the test to:
doThrow(new ResponseStatusException(HttpStatus.NOT_FOUND))
.when(userService).getUser(anyString());
...
MvcResult s = mvc.perform(get("/api/user")
...
.param("username","Test")
...
...
verify(userService).getUser("Test");
As an aside: in genereal, the mock setup should use the form
when(...).then(Return|Throw)(...)
instead of
do(Return|Throw)(...).when(...)...
since the former guarantees type safety. The latter should only be used if not possible otherwise (e.g. throwing an Exception on a void method).
With this in mind, we could rewrite the mock setup to
when(userService.getUser(anyString()))
.thenThrow(new ResponseStatusException(HttpStatus.NOT_FOUND));

Service call into Controller in Test not updating field correctly

I try some tests on my UserController for updating the email field like this :
#Test
public void testUpdateUserNominal() throws Exception {
savedUser.setEmail(EMAIL_2);
UserDTORequest updatedUserDTORequest = modelMapper.map(savedUser, UserDTORequest.class);
String jsonMessage = gson.toJson(updatedUserDTORequest);
mvc.perform(put(USER_ROUTE)
.contentType(APPLICATION_JSON)
.accept(APPLICATION_JSON)
.content(jsonMessage))
.andExpect(status().isOk())
.andReturn();
assertThat(userService.findAll().size()).isEqualTo(1);
assertThat(userService.findAll().get(0).getEmail()).isEqualTo(EMAIL_2);
}
Here is the code on my controller :
#Override
public ResponseEntity<UserDTOResponse> update(UserDTORequest userDTORequest) throws ParseException {
Optional<User> optUser = userService.findByUri(userDTORequest.getUri());
if(optUser.isEmpty()){
return ResponseEntity
.notFound()
.build();
}
User user = convertToEntity(userDTORequest);
User userUpdated = userService.save(user);
UserDTOResponse userDTOResponse = convertToDto(userUpdated);
return ResponseEntity
.ok(userDTOResponse);
}
The response from the mockMvc is correct : the new email set is the good one.
But on the second assertThat :
assertThat(userService.findAll().get(0).getEmail()).isEqualTo(EMAIL_2);
The email is not the good one, the email is not updated.
What I do wrong ?
Thanks :)

How to send Status Codes Along with my Custom Class Using Spring?

I am trying to make a log in system using spring. Problem is if username is not in the database I want to send a different status code and if username is in the database but password is wrong I want to send different status code. Because in my front end i am going to inform user using different alerts according to status code.
I cannot use HttpStatus.NOT_ACCEPTABLE or something like that because my controller is returning a User(my custom class). It will either return User or null.
#GetMapping("/users")
public User userLogin(#RequestParam String username,#RequestParam String password) {
User user = userService.findByUsername(username);
if(user==null) {
return null;
}
if(user.getPassword().equals(password)) {
return user;
} else {
return null;
}
}
Here I am trying to change status while returning nulls.
you can return ResponseEntity to meet your requirement
#GetMapping("/users")
public ResponseEntity<User> userLogin(#RequestParam String username,#RequestParam String password) {
User user = userService.findByUsername(username);
if(user==null) {
return new ResponseEntity<>(null,HttpStatus.NOT_FOUND);
}
if(user.getPassword().equals(password)) {
return new ResponseEntity<>(user,HttpStatus.OK);
} else {
return new ResponseEntity<>(null,HttpStatus.FORBIDDEN);
}
}
Spring 5 introduced the ResponseStatusException class. We can create an instance of it providing an HttpStatus and optionally a reason and a cause:
#GetMapping(value = "/{id}") public Foo findById(#PathVariable("id") Long id, HttpServletResponse response) {
try {
Foo resourceById = RestPreconditions.checkFound(service.findOne(id));
eventPublisher.publishEvent(new SingleResourceRetrievedEvent(this, response));
return resourceById;
}
catch (MyResourceNotFoundException exc) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Foo Not Found", exc);
} }
Maybe this is which you looking for?
Detail in https://www.baeldung.com/exception-handling-for-rest-with-spring#controlleradvice

Categories