I've written test for update method in service layer and it always throws an exception "User with the this id is not found" and didn't work.
I've tried to add optional but it didn't work too.
Could you give me a little piece of advice please? What should I fix in my test?
My testing method looks like:
#Override
public UserDTO updateUser(String id, UserDTO updatedUser) {
Optional<UserEntity> databaseUser = userRepository.findById(Integer.valueOf(updatedUser.getUserName()));
if (databaseUser.isEmpty()) {
throw new UserNotFoundException("User with the this id is not found");
}
UserEntity entity = mapToUserEntity(updatedUser);
return map(userRepository.save(entity));
}
My test looks like:
#Test
void updateUserTest(){
final int id = 1;
final long roleId = 2L;
UserDTO userDto = new UserDTO();
userDto.setUserName(String.valueOf(12));
userDto.setId(String.valueOf(id));
userDto.setName(new UserDTO.Name("surname", "firstname", "patronymic"));
userDto.setActive(true);
userDto.setEmails(List.of(new UserDTO.Email("email", "external")));
userDto.setRoles(List.of("2"));
userDto.setLastAccessDate(LocalDateTime.of(2022, 10, 25, 4, 20));
userDto.setUnit(null);
when(roleRepository.findById(any())).thenReturn(Optional.of(new UserDTO().setId(roleId)));
UserEntity userEntity = userService.mapToUserEntity(userDto);
when(userRepository.save(any())).thenReturn(userEntity.setId(id));
userService.updateUser(String.valueOf(id), userDto);
var actualUser = userService.updateUser(String.valueOf(id), userDto);
userDto.setUserName(String.valueOf(id));
assertEquals(actualUser, userDto);
}
The updateUser method calls userRepository.findById(Integer.valueOf(updatedUser.getUserName())).
The test does not stub this method, so the userRepository mock object will use the default answer behaviour to return a value. The example code does not show how the userRepository mock is created, but if the default answer is not customised, it will use RETURNS_DEFAULTS and return an empty Optional by default for methods returning Optional.
To make it return userDto instead, you should stub the method before calling userService.updateUser in the test:
when(userRepository.findById(12)).thenReturn(Optional.of(userEntity));
when(userRepository.save(any())).thenReturn(userEntity.setId(id));
userService.updateUser(String.valueOf(id), userDto);
Related
I know that I could put the annotations on age field.
But I want to validate age in setAge method, here is my code:
class UserController {
#PutMapping
public ResponseEntity partialUpdate(#RequestBody #Validated UserDTO userDTO) {
return userService.partialUpdate(userDTO);
}
}
public class UserDTO {
private String username;
private Integer age;
public void setAge(#Min(0) #Max(100) Integer age) {
this.age = age;
}
}
It's valid when I give age=101, seems the validations not work.
Seems like you need to use the Validator to check whether you class is valid.
If you are creating a object of UserDTO.
UserDTO user = new UserDTO("My Name", 101);
Then perform the following
Validator validator = Validation.buildDefaultValidatorFactory.getValidator();
Set<ConstraintViolation<SomeEntity>> errors = validator.validate(user);
Then based on your requirement do the necessary things like notify client what was the wrong field, log them. If empty then it seems like everything is valid and proceed further.
if(CollectionUtils.isNotEmpty(errors)){
throw new ConstraintViolationException(errors);
}
I am writing a junit test case for my service method which has an external call to jpa repository, which I would like to mock.
This method findall(Pageable pageable) returns a page of entities which is being mapped by Mapstruct's mapper.
However, I get an NPE when I assert the return.
There might be something which I am missing, I am not sure how to mock this method call.
I tried writing test case like this
Test case:
public class myTestclass {
#Test
public void testFindAllUser() {
User user1 = new User();
user1.setId(Long.valueOf(1));
User user2 = new User();
user2.setId(Long.valueOf(2));
List<User> userList = new ArrayList<>();
userList.add(user1);
userList.add(asset2);
Pageable pageable = PageRequest.of(0, 5);
Page<User> userPage = new PageImpl<>(userList, pageable, userList.size());
Page<UserDto> userDtoPage = null;
Mockito.when(userRepositoryMock.findAll(pageable)).thenReturn(userPage);
Mockito.when(userPage.map(userMapperMock::toDto)).thenReturn(userDtoPage);// expecting to mock this object in
// some other way.
assertThat(userService.findAll(pageable)).isEqualTo(userDtoPage); // throws NPE
}
}
The method for which i am writing the test case:
public Page<UserDto> findAll(Pageable pageable)
{
return userRepository.findAll(pageable).map(userMapper::toDto);
}
This is my mapper class:
#Mapper(componentModel = "spring", uses = { FarmerMapper.class })
public interface UserMapper extends EntityMapper<UserDto, User> {
UserDto toDto(User user);
User toEntity(UserDto userDto);
}
What is the right way to mock the mapper method toDto so that it returns a page of userDto?
Since the .map uses UserMapper's toDto Method to convert each element of the page, I mocked multiple elements of userMapper to resolve this problem.
Hence I created 2 pages, one for User and another for UserDto.
So my test case was modified as
#Test
public void testFindAllUser() {
User user1 = new User();
user1.setId(DEFAULT_ID);
User user2 = new User();
user2.setId(2L);
List<User> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
Pageable pageable = PageRequest.of(0, 5);
Page<User> userPage = new PageImpl<>(userList, pageable, userList.size());
UserDto userDto1 = new UserDto();
userDto1.setId(Long.valueOf(1));
UserDto userDto2 = new UserDto();
userDto2.setId(Long.valueOf(2));
List<UserDto> userDtoList = new ArrayList<>();
userDtoList.add(userDto1);
userDtoList.add(userDto2);
Page<UserDto> userDtoPage = new PageImpl<>(userDtoList, pageable, userDtoList.size());
Mockito.when(userMapperMock.toDto(user1)).thenReturn(userDto1);
Mockito.when(userMapperMock.toDto(user2)).thenReturn(userDto2); //Mocking the toDto method.
Mockito.when(userRepositoryMock.findAll(pageable)).thenReturn(userPage);
assertThat(userService.findAll(pageable)).isEqualTo(userDtoPage);
}
Now, if there are more number of elements to be added to the Page, I would mock the same toDto for all of the elements.
I want to edit my User Class while passing the id and while returning user object to controller it is getting error such as "There was an unexpected error (type=Internal Server Error, status=500)".It is telling me to typecast to Optional.I don't know what to do.
UserService Class
public User editMyUser(int id) {
return userRepository.findById(id);
}
Controller Class
#RequestMapping("/edit-user")
public String editUser(#RequestParam int id, HttpServletRequest request) {
userService.deleteMyUser(id);
request.setAttribute("user", userService.editMyUser(id));
request.setAttribute("mode", "MODE_UPDATE");
return "welcome";
}
This is how findById looks like in the new version of Spring (according to docs):
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
Optional<T> findById(ID primaryKey);
// .... other methods ...
}
So, the first thing I would change in your code is :
public Optional<User> editMyUser(int id) {
return userRepository.findById(id);
}
Make your method return Optional<User>, maybe this will help.
Also, be careful when using user returned by that new method, e.g. here
request.setAttribute("user", userService.editMyUser(id));
With Optional you need to use get() to obtain the actual user instance:
userService.editMyUser(id).get()
but first, you should check if that Optional actually contains the user:
Optional<User> optionalUser = userService.editMyUser(id);
if (optionalUser.isPresent()) {
User user = optionalUser.get();
// do whatever you need
} else {
// means user is null - nothing found for ID
// act accordingly
}
There is also a good documentation that Spring provides. Could be useful.
Happy Coding :)
I let u here the cofiguration of the interface for a rest service that is working rihg now
#Api(value = "empleados", description = "the empleados API")
public interface EmpleadosApi {
#ApiOperation(value = "Buscar empleados", notes = "", response = ResultadoBusquedaEmpleado.class, tags = {})
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Búsqueda de empleados", response = ResultadoBusquedaEmpleado.class) })
#RequestMapping(value = "/empleados", method = RequestMethod.GET)
ResponseEntity<ResultadoBusquedaEmpleado> empleadosGet(
#ApiParam(value = "Nombre de empleado") #RequestParam(value = "nombre", required = false) String nombre)
}
I have the following method on a service class:
#Service
public class Service {
(...)
public Page<ChannelAccount> getByCustomerAndChannelType(Pageable pageable, Customer customer, ChannelType channelType) {
return channelAccountRepository.findByCustomerAndChannelType(pageable, customer, channelType);
}
}
This returns the expected result. Now I trying to build the unit test for it. So far I got this:
#RunWith(MockitoJUnitRunner.class)
public class ChannelAccountServiceTest {
#InjectMocks
private ChannelAccountService channelAccountService;
#Mock
private ChannelAccountRepository channelAccountRepository;
(...)
#Test
public void testGetByCustomerAndChannelTypePageable() {
Page<ChannelAccount> pageResult = new PageImpl<>(channelAccountService.getAllChannelAccounts());
Mockito.when(channelAccountRepository.findByCustomerAndChannelType(pageable, customer, ChannelType.FACEBOOK)).thenReturn(pageResult);
Page<ChannelAccount> channelAccountPage = channelAccountRepository.findByCustomerAndChannelType(pageable, customer, ChannelType.FACEBOOK);
assertEquals(pageResult, channelAccountPage);
}
Somehow this doesn't feels right. What am I missing here?
Not sure why you are calling this method as it has nothing to do with the case itself:
Page<ChannelAccount> pageResult = new PageImpl<>(channelAccountService.getAllChannelAccounts());
I would do the following in the test:
Pageable pageableStub = Mockito.mock(Pageable.class);
Page pageStub = Mockito.mock(Page.class);
Mockito.when(channelAccountRepository
.findByCustomerAndChannelType(pageableStub, customer, ChannelType.FACEBOOK))
.thenReturn(pageStub);
Page<ChannelAccount> channelAccountPage = channelAccountService
.findByCustomerAndChannelType(pageableStub, customer, ChannelType.FACEBOOK);
assertTrue(pageResult == channelAccountPage);
I would check whether the objects are the same instances instead of equals (even more strict).
I have controller:
#Controller
public class EventMenuController{
#RequestMapping(value = "/updateEvent", method = RequestMethod.POST)
public String updateEvent(Model model,
#Valid #ModelAttribute("existedEvent") Event event,
BindingResult result,
#ModelAttribute("linkedCandidates") Set<Candidate> candidates,
#ModelAttribute("linkedvacancies") Set<Vacancy> vacancies,
#RequestParam(required = true, value = "selectedEventStatusId")Integer EventStatusId,
#RequestParam(required = true, value = "selectedEventTypeId")Integer EventTypeId ,
RedirectAttributes attributes) {
if (result.hasErrors()) {
//model.addAttribute("idEvent", event.getId());
event.setCandidates(candidates);
event.setVacancies(vacancies);
return "eventDetails";
}
eventService.updateEventAndLinkedEntities(event, candidates, vacancies ,EventTypeId,EventStatusId);
attributes.addAttribute("idEvent",event.getId() );//event is null therefore NPE here
attributes.addAttribute("message", "submitted correctly at "+new Date());
return "redirect:eventDetails";
}
}
For testing this method I wrote following class:
#ContextConfiguration(locations = { "classpath:/test/BeanConfigUI.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
public class EventMenuControllerTest {
#Test
public void updateEvent() throws Exception{
MockHttpServletRequestBuilder request = MockMvcRequestBuilders
.post("/updateEvent");
request.param("selectedEventStatusId", "1");
request.param("selectedEventTypeId", "1");
EventMenuController eventMenuController = (EventMenuController) wac.getBean("eventMenuController");
EventService mockEventService = Mockito.mock(EventService.class);
eventMenuController.eventService = mockEventService;
Mockito.doNothing().when(mockEventService).updateEventAndLinkedEntities(any(Event.class), any(Set.class),any(Set.class), any(Integer.class), any(Integer.class));
ResultActions result = mockMvc.perform(request);
result.andExpect(MockMvcResultMatchers.view().name("redirect:eventDetails"));
result.andExpect(MockMvcResultMatchers.model().attributeExists("idEvent"));
result.andExpect(MockMvcResultMatchers.model().attributeExists("message"));
}
}
In the process request executing on server side I see error that show me that event object is null.
Question:
What request I must write that pass event to server-side(controller method) using MockMvc ?
The Event class object is not initialized. You can either create an Event object or create a mock of an Event object depending on your test case and send it to the EventMenuController class object. You can do this similar to how you have sent a mock object of EventService to EventMenuController.
It is better practice to use fields as part of a class and not as part of a method. This will give you flexibility to mock any field.