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.
Related
I have to write a test function for findInContextUser based on JUnit Mock in Spring Boot but I have no idea how to write it.
How can I write findInContextUser for Junit Test?
Here are my code defined in UserService shown below.
public UserDto getUserDto(String username) {
var user = findUserByUsername(username);
return UserDto.builder()
.id(user.getId())
.username(user.getUsername())
.role(user.getRole())
.build();
}
public UserDto findInContextUser() {
final Authentication authentication = Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication()).orElseThrow(notFoundUser(HttpStatus.UNAUTHORIZED));
final UserDetails details = Optional.ofNullable((UserDetails) authentication.getPrincipal()).orElseThrow(notFoundUser(HttpStatus.UNAUTHORIZED));
return getUserDto(details.getUsername());
}
private static Supplier<GenericException> notFoundUser(HttpStatus unauthorized) {
return () -> GenericException.builder().httpStatus(unauthorized).errorMessage("user not found!").build();
}
Here is my test class shown below.
#Test
void itShouldFindInContextUser(){
// given - precondition or setup
User user = User.builder()
.username("username")
.password("password")
.role(Role.USER)
.build();
UserDto expected = UserDto.builder()
.id(user.getId())
.username(user.getUsername())
.role(user.getRole())
.build();
var roles = Stream.of(user.getRole())
.map(x -> new SimpleGrantedAuthority(x.name()))
.collect(Collectors.toList());
UserDetails details = new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), roles);
Authentication authentication = Mockito.mock(Authentication.class);
SecurityContext securityContext = Mockito.mock(SecurityContext.class);
// when - action or the behaviour that we are going test
when(securityContext.getAuthentication()).thenReturn(authentication);
when(securityContext.getAuthentication().getPrincipal()).thenReturn(details);
// then - verify the output
UserDto actual = userService.findInContextUser(); // ERROR IS HERE
assertEquals(expected, actual);
assertEquals(expected.getUsername(), actual.getUsername());
verify(userService, times(1)).findInContextUser();
}
Here is the error message shown below.
com.example.lib.exception.GenericException
Debug Part : 401 UNAUTHORIZED
I also added #WithMockUser(username = "username", password = "password", roles = "USER") but nothing changed.
Here is the solution shown below.
Add this line shown below underneath when parts
SecurityContextHolder.setContext(securityContext);
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 {
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 :)
I am writing unit test cases for one of my methods which performs GET Request(query to external system), and receives query results which i store in my model object, I am not able to mock the rest template exchange. Need some help with it.
The below code includes my method and also my test class for the method.
public Car getCarModelDetails(String id) {
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
ResponseEntity<QueryResultCar> exchange = restTemplate.exchange(
config.restUrl + "/v" + config.restVersion + /query?q=SELECT + SELECT_COLUMNS
+ " FROM Car WHERE (Model = '" + id + "')",
HttpMethod.GET, entity, QueryResultCar.class);
if (exchange.getStatusCode().equals(HttpStatus.OK)) {
List<Car> records = exchange.getBody().records;
if (records != null && records.size() == 1) {
return records.get(0);
} else (records == null || records.isEmpty()) {
return null;
}
} else {
throw new RuntimeException();
}
}
private static class QueryResultCar extends QueryResult<Car> {
}
#Test
public void getCarModelDetails_valid() throws JSONException {
String id = null;
HttpEntity<String> entity = new HttpEntity<>("parameters", headers);
new ResponseEntity<>("", HttpStatus.OK);
Mockito.when(restTemplate.exchange(config.restUrl + "/v" + config.restVersion + /query?q=SELECT + SELECT_COLUMNS
+ " FROM Car WHERE (Model = '" + id + "'), HttpMethod.GET, entity, QueryResultCar.class))
.thenReturn(response);
}
You need to use matchers and probably need to use verify and and arg captor to check all the things you want. I would probably divide this test up because it has many assertions, but this should get you started.
#RunWith(MockitoJUnitRunner.class)
public class SubjectTest {
#InjectMocks
private CarCar subject;
#Mock
private RestTemplate restTemplate;
#Test
public void getCarModelDetails_valid() throws JSONException {
String id = "123";
Config config = new Config();
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(QueryResultCar.class)))
.thenReturn(new ResponseEntity<>(new QueryResultCar(), HttpStatus.OK));
Car actual = subject.getCarModelDetails(id);
ArgumentCaptor<HttpEntity> httpEntityArgumentCaptor = ArgumentCaptor.forClass(HttpEntity.class);
verify(restTemplate).exchange(eq(config.restUrl + "/v" + config.restVersion + "/query?q=SELECT + SELECT_COLUMNS"
+ " FROM Car WHERE (Model = '" + id + "')"), eq(HttpMethod.GET), httpEntityArgumentCaptor.capture(), eq(QueryResultCar.class));
assertEquals(APPLICATION_JSON_VALUE, httpEntityArgumentCaptor.getValue().getHeaders().get("Accept").get(0));
assertEquals("Car to string", actual.toString());
}
}
Object reference of "entity" in your unit test method and real method is different. You need to handle mock for "entity"
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.