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;
Related
#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);
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));
I want to InjectMocks into my interface and not my service class , so far its not possible with Mockito (I am using Mockito 2.8.9). I have used SpringBoot and using #MockBean I am able to test interfaces of services but with #Mock of SpringMvc I can only test a concrete class, why and what can I do to test services?
Any suggestion on how to get this to interface level.
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(classes = EmployeeServiceImpl.class)
public class EmployeeServiceTest {
#org.mockito.Mock
private EmployeeDao employeeDao;
#InjectMocks
private EmployeeServiceImpl employeeService ;
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void SaveEmployee() throws Exception {
Employee employee = new Employee();
employee.setEmployeeName("valentine");
employee.setSalary(400);
employee.setDepartmentId(1);
employee.setEmployeeId(1);
Employee employee1 ;
when(employeeDao.addEmployee(employee)).thenReturn(employee);
employee1 = employeeService.saveEmployee(employee);
org.junit.Assert.assertNotNull(employee);
assertEquals(employee1.getSalary(), employee.getSalary());
Mockito.verify(employeeDao).addEmployee(employee);
}
So i solved this by first creating Bean in Config class that returns interface of Dao
#Profile("test")
#Configuration
public class Config {
#Bean
#Primary
public EmployeeDao employeeDao(){
return Mockito.mock(EmployeeDao.class);
}
}
Then i used the profile in my test and set configContxt TO my Config Class
#ActiveProfiles("test")
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes ={ EmployeeServiceImpl.class, Config.class})
public class EmployeeServiceTest1 {
#Autowired
private EmployeeDao employeeDao;
#Autowired
private EmployeeService employeeService;
I have below Test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringTestConfig.class)
public class UserServiceTest {
#Inject
private UserRepository userRepository;
#Inject
private UserService userService;
#Test
public void testProcessInvoice() throws SQLException {
User user = new User();
user.setFirstName("abc");
when(userRepository.save(any(User.class))).thenReturn(user);
Assert.assertNotNull(userService);
User savedUser = userService.save(user);
Assert.assertEquals("abc", savedUser.getFirstName());
}
}
I have below SpringTestConfig.java
#Configuration
public class SpringTestConfig {
#Bean
public UserService userService() {
return Mockito.mock(UserService.class);
}
#Bean
public UserRepository userRepository() {
return Mockito.mock(UserRepository.class);
}
}
call to User savedUser = userService.save(user); returns null user object. I am not able to figure it out why it is returning null.
EDIT:
UserRepository is JpaRepository, if this is a problem
public interface UserRepository extends JpaRepository<User, Long> {
}
Your UserService is a mock object, and has no defined behavior for dealing with the #save(User) method.
Mocking the object under test is probably not what you are after here. I would recommend your objects under test are instantiated in the test, and injected with the mocks or stubs of the objects that they utilize.
Your configuration needs to return a real UserService:
#Configuration
public class SpringTestConfig {
#Bean
public UserService userService() {
return new UserServiceImpl(); // or whatever your implementation is
}
#Bean
public UserRepository userRepository() {
return Mockito.mock(UserRepository.class);
}
}
Mocks are for collaborators, not for the thing you're testing.
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);
}
}