How to inject mapper (mapstruct) to Junit test with SpringJUnit4ClassRunner - java

I have problems with configuring tests for SpringJUnit4ClassRunner.class).
My problem is because my mapper from map struct returns null when reached.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
class UserServiceTestSuite {
#Spy
private UserDto userDto;
#Spy
private UserMapper userMapper;
#Mock
private UserRepository userRepository;
#InjectMocks
private UserService userService;
private User createUser() {
return User.builder()
.firstName("Steve")
.lastName("Jobs")
.login("SteveJobs")
.password("password")
.role(UserRole.ROLE_ADMIN)
.build();
}
#Test
public void testCreateUser() {
//Given
User user = createUser();
Mockito.when(userRepository.save(user)).thenReturn(user);
//When
UserDto userDto = userService.createUser(userMapper.mapToUserDto(user));
Long id = userDto.getId();
//Then
Assert.assertEquals("Steve", userDto.getFirstName());
Assert.assertEquals("Jobs", userDto.getLastName());
Assert.assertEquals("SteveJobs", userDto.getLogin());
Assert.assertEquals("ROLE_ADMIN", userDto.getRole());
}

In my opinion you have two options:
Inject the mapper via #SpringBootTest(classes = {UserMapperImpl.class}) and
#Autowired
private UserMapper userMapper;
Simply initialize the Mapper private UserMapper userMapper = new UserMapperImpl() (and remove #Spy)
When using the second approach you can even remove the #SpringBootTest because in the given snippet you do not need a Spring context (created by the annotation).
#RunWith(MockitoJUnitRunner.class) can be used to automatically inject objects annotated with #Mock into your UserService. Writing unit tests without creating a spring context helps to keep test suite execution time low.

public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserDTO userMapper.mapToUserDto(Object value);
}
Using this mapper interface you can instantiate from the mock and use the methods.
For example:
#Mock
private UserMapper userMapper;
Init the mocks:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
and you use the call INSTANCE
UserDto userDto = userService.createUser(userMapper.INSTANCE.mapToUserDto(user));

Related

MapStruct not working in service class while running Test cases

I am creating test cases, in one of my service class method I am using mapStruct to map entity into dto class.
This is my mapper class
#Mapper(componentModel = "spring")
public interface UserMapper {
List<UserDto> toUserDto(List<UserEntity> users);
}
below is how I am injecting in my service class
#Service
#RequiredArgsConstructor
public class UserServiceImpl implements UserService{
private final UserMapper userMapper;
This is how I am using it
List<UserDto> userDto = userMapper.toUserDto(lst);
this is how I am doing it in my Test class
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(classes = Application.class)
public class ApplicationTest {
#Mock
private UserRepository userRepo;
#Mock
private UserMapper userMapper;
#InjectMocks
private UserServiceImpl userServiceImpl;
#Test
public void contextLoads() {
then(controller).isNotNull();
then(userServiceImpl).isNotNull();
}
#Test
public void getAllUser() {
List<UserEntity> lst = new ArrayList<UserEntity>();
UserEntity userOne = new UserEntity();
userOne.setEmpFullname("Test Test1");
userOne.setUserId("marina");
userOne.setFlag("Y");
UserEntity usertwo = new UserEntity();
usertwo.setEmpFullname("Test Test2");
usertwo.setUserId("test");
usertwo.setFlag("Y");
lst.add(userOne);
lst.add(usertwo);
when(userRepo.findByStatus("W")).thenReturn(lst);
try {
List<UserDto> pendingUsersList = userServiceImpl.getPendingUser();
assertEquals(2, pendingUsersList.size());
} catch (GeneralException e) {
e.printStackTrace();
}
}
}
when I am running my test cases I am able to see these 2 records in entity class but when this line executes
List<UserDto> userDto = userMapper.toUserDto(lst); it gives me blank array.
Note - In my entity Class I have many fields but from test class I am passing only 3 parameters.
You have annotated your UserMapper with a #Mock annotation, without writing the mockito configuration for this mock. Then the blank array is expected.
Remove the #Mock annotation, or specify what should be returned by the mock.
For example :
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(classes = Application.class)
public class ApplicationTest {
#Mock
private UserRepository userRepo;
#Spy
private UserMapper userMapper = Mappers.getMapper(UserMapper.class);
#InjectMocks
private UserServiceImpl userServiceImpl;

Mocked service function not working as expected

Here is my code. I can't see why it is not working. The problem is with the line in test :
when(applicationUserRepository.findApplicationUserByUsername("testUser"))
.thenReturn(userToReturnFromRepository);
doesn't seem to be doing anything. The function findApplicationUserByUsername returns an empty optional when it should be returning an optional of userToReturnFromRepository.
Controller:
#RestController
#RequestMapping("api/v1/exercises")
public class ExerciseController {
#Autowired
ExerciseService exerciseService;
#GetMapping
public List<Exercise> getExercises() {
List<Exercise> exercises = exerciseService.getAllExercises();
return exercises;
}
}
Service:
#Service("exerciseService")
public class ExerciseService {
#Autowired
ExerciseRepository exerciseRepository;
#Autowired
ApplicationUserRepository applicationUserRepository;
#Transactional
public List<Exercise> getAllExercises() {
Principal principal = SecurityContextHolder.getContext().getAuthentication();
Optional<ApplicationUser> applicationUser = applicationUserRepository.findApplicationUserByUsername(principal.getName());
List<Exercise> exercises = new ArrayList<>();
if(applicationUser.isPresent()){
exercises = applicationUser.get().getExercises();
}
return exercises;
}
}
The test:
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
public class ExerciseControllerTest {
private final MockMvc mockMvc;
private final ObjectMapper objectMapper;
#Mock
ApplicationUserRepository applicationUserRepository;
#Autowired
public ExerciseControllerTest(MockMvc mockMvc,
ApplicationUserRepository applicationUserRepository, ObjectMapper objectMapper) {
this.mockMvc = mockMvc;
this.applicationUserRepository = applicationUserRepository;
this.objectMapper = objectMapper;
}
#BeforeEach
public void initMocks() {
MockitoAnnotations.openMocks(this);
}
#Test
#WithMockUser(username = "testUser")
public void testGetExercises() throws Exception {
Exercise ex = new Exercise();
ex.setData("test");
ApplicationUser user = new ApplicationUser();
Exercise[] exercises = {ex};
List<Exercise> list = new ArrayList<Exercise>(Arrays.asList(exercises));
user.setExercises(list);
Optional<ApplicationUser> userToReturnFromRepository = Optional.of(user);
when(applicationUserRepository.findApplicationUserByUsername("testUser"))
.thenReturn(userToReturnFromRepository);
mockMvc.perform(get("/api/v1/exercises")).andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(1)));
}
}
There are two conflicting things happening in your test:
You are using Mockito to init a mock implementation via reflection
You have ApplicationUserRepository being Spring injected into the Test class via the constructor.
What ends up happening is this:
spring injects applicationUserRepository into the constructor param
The applicationUserRepository field is set to the spring injected version in the constructor
Mockito inits a new applicationUserRepository mock
Mockito replaces the applicationUserRespository field with the mock (i.e. goodbye to your handle on the spring bean that your MVC setup is using!)
The easiest way I can think of to fix it is to use #MockBean instead of the #Mock combined with Constructor injection. #MockBean will instruct Spring to create the mock instance for you, use it, and provide it to you in the test.
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
public class ExerciseControllerTest {
private final MockMvc mockMvc;
private final ObjectMapper objectMapper;
#MockBean // User MockBean instead of Mock
ApplicationUserRepository applicationUserRepository;
#Autowired
public ExerciseControllerTest(MockMvc mockMvc, ObjectMapper objectMapper) {
this.mockMvc = mockMvc;
//remove the applicationUserRepository injection
this.objectMapper = objectMapper;
}
// remove MockitoAnnotations.openMocks(this);
//...
...
}

I have a question while writing the test code. feat JUnit

#RequiredArgsConstructor
#Service
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
#Transactional
#Override
public Long insertUser(UserSaveRequestDto userSaveRequestDto) {
Long user_id = userMapper.insertUser(userSaveRequestDto);
return user_id;
}
}
I'm trying to test the service layer.
I wonder which one should be further verified.
The code below simply verified whether the userMapper's insertUser was called or if the parameters were properly contained, what additional verification should be made?
#ExtendWith(MockitoExtension.class)
public class UserServiceTest {
#Mock
UserMapper userMapper;
#InjectMocks
UserServiceImpl userService;
UserSaveRequestDto userSaveRequestDto;
UserResponseDto userResponseDto;
#BeforeEach
void setUp() {
userSaveRequestDto = UserSaveRequestDto.builder()
.userName("test")
.userPhoneNumber("01026137832")
.build();
userResponseDto = UserResponseDto.builder()
.userId(1L)
.userName("test")
.userPhoneNumber("01026137832")
.build();
}
// Mockito 이용한 테스트 코드
#DisplayName("Mock을 사용한 insertUser 테스트")
#Test
public void insertUser() {
// given
// when
Long userId = userService.insertUser(userSaveRequestDto);
// then
ArgumentCaptor<UserSaveRequestDto> captor = ArgumentCaptor.forClass(UserSaveRequestDto.class);
then(userMapper).should().insertUser(captor.capture());
assertThat(captor.getValue()).isEqualTo(userSaveRequestDto);
}
}
You should test if the returned userId has the expected value. Tell the userMapper mock to return a specific value and assert that the userService returned it as well.
assertThat(userId).isEqualTo(expectedIdValue);

DAO method is not being mocked

I have a UserDAO that has methods like add,delete,update,getUser and getUsers (to manipulate my database). I also have a Requestmapping that I want to test via Mockito. Here is everything relevant in terms of what I have:
The test:
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(value = UserController.class)
public class UserControllerTest
{
#Autowired
private MockMvc mockMvc;
#Mock
private UserDAO userDao;
#InjectMocks
private UserController userController;
#Before
public void setUp()
{
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(userController)
.build();
}
#Test
public void testGetAllUsersSuccess() throws Exception{
User s = new User();
List<User> users = Arrays.asList(
new User(1,"somebody", "pass", "somebody#yahoo.com"),
new User(2, "sam", "mypass", "tom#hotmail.com"));
doReturn(users).when(userDao).getUsers();
mockMvc.perform(get("/userList"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].userID", is(1)))
.andExpect(jsonPath("$[0].name", is("somebody")))
.andExpect(jsonPath("$[0].password", is("pass")))
.andExpect(jsonPath("$[0].email", is("somebody#yahoo.com")))
.andExpect(jsonPath("$[1].userID", is(2)))
.andExpect(jsonPath("$[1].name", is("sam")))
.andExpect(jsonPath("$[1].password", is("mypass")))
.andExpect(jsonPath("$[1].email", is("tom#hotmail.com")));
verify(userDao, times(1)).getUsers();
verifyNoMoreInteractions(userDao);
}
}
UserController where I have my requestmapping:
#RestController
public class UserController {
/**
*
*
* #return list of all users
*/
#RequestMapping(value = "/userList", method = RequestMethod.GET)
public List<User> user() throws Exception {
UserDAO gettingUsers = new UserDAO();
return gettingUsers.getUsers();
}
}
the getUsers() method:
public List<User> getUsers(){
try(Session session = HibernateUtil.getSessionFactory().openSession()){
return session.createQuery("from User", User.class).getResultList();
}
}
PROBLEM: When I execute my test, a connection is made with the database (which is not what I want) instead of a fake instance of UserDAO that only returns the users list that I have made in mockito.
QUESTION: What should my code look like to be able to mock the userDAO method getUsers() such that it doesn't return the users from the database but instead returns the faked users list when I call it by requesting the get method of "/userList"?
UPDATE=
SOLUTION:
My new Controller:
#RestController
public class UserController {
private UserDAO userDAO;
public UserController (UserDAO userDAO)
{
this.userDAO = userDAO;
}
/**
*
*
* #return list of all users
*/
#GetMapping(value = "/Users", method = RequestMethod.GET)
public List<User> users() throws Exception {
return userDAO.getUsers();
}
}
Changes done in test:
//...
#MockBean
private UserDAO userDao;
....
when(userDao.getUsers()).thenReturn(users);
...//
Spring didn't find my userDAO Bean so I added the package name where I stored the bean in the ApplicationConfiguration class of mine. Doing this, Spring was able to inject the bean in the constructor.
The userDao mock is never set to the controller under test.
1) You don't want to use new to instantiate the UserDAO in your Controller :
UserDAO gettingUsers = new UserDAO();
You want to inject it as a Spring Bean.
Besides you also have to make the dependency visible because actually it is not settable from the unit test POV.
You could inject it in the constructor :
private UserDAO userDAO;
public UserController (UserDAO userDAO){
this.userDAO = userDAO;
}
2) You mock Spring bean in a running container with #MockBean from Spring Boot test not with #Mock from Mockito.

#InjectMocks and non-mocked objects (Spring Data Repositories)

Is there any way to inject non-mocked objects with #InjectMocks?
My Setup has a UserSignupService with dependencies to a MailService and a UserRepository (a Spring Data Repository). I've a unit test creating a spy of the MailService and I annotated the UserSignupService with #InjectMocks. Sadly this won't inject the UserRepository (non-mocked) into the service class.
Combining #InjectMocks with #Autowired won't inject the mocked MailService, but the bean from the application context.
MockitoAnnotations.initMocks() is run in AbstractServiceTest.setUp(). This class also holds the configuration of the the unit test (SpringJunit4TestRunner, etc.)
public class UserSignupServiceTest extends AbstractServiceTest {
#Autowired #Spy
private MailService<UserActivationMail> mailService;
#InjectMocks
private UserSignupServiceImpl service;
#Before
public void setUp() throws Exception {
super.setUp();
}
}
#Service
public class UserSignupServiceImpl implements UserSignupService {
private final UserRepository repository;
private final MailService<UserActivationMail> mailService;
#Autowired
public UserSignupServiceImpl(UserRepository repository,
MailService<UserActivationMail> mailService) {
this.repository = repository;
this.mailService = mailService;
}
//methods...
}
You need to initialize your Mockito MockitoAnnotations.initMocks(this);
Here is sample Spring JUnit test class I have
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:config/spring-context.xml" })
public class SpringKPTest {
#Autowired
SomeClassA SomeClassA;
#Mock
SomeClassB SomeClassB;
#Before
public void init(){
MockitoAnnotations.initMocks(this);
}
}

Categories