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.
Related
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;
In tests i mock DateService to have the same date every time when i run the test, but when i use DateServie in other service then the mock retun null all the time. It is strange because the mock works in my custom date time provder. Here is the code:
Its work here:
#Service(MyDateTimeProvider.MY_DATE_TIME_PROVIDER)
public class MyDateTimeProvider implements DateTimeProvider {
public static final String MY_DATE_TIME_PROVIDER = "MyDateTimeProvider";
#Autowired
private DateService dateService;
#Override
public Optional<TemporalAccessor> getNow() {
return Optional.of(dateService.getCurrentDate().toInstant());
}
}
#Service
public class DateService {
public Date getCurrentDate() {
return new Date();
}
}
Its not work in the UserService:
#SpringBootTest
public class Test{
#MockBean
protected DateService dateService;
#BeforeEach
public void beforeEach() { Mockito.when(dateService.getCurrentDate()).thenReturn(DEFAULT_DATE_TIME.toDate());
}
...
}
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private DateService dateService;
private User createNewUser(final UserDto dto) {
User user = new User();
user.setEmail(dto.getEmail());
user.setRegistrationDate(dateService.getCurrentDate()); // i got null here
return userRepository.save(user);
}
}
What did i wrong? Thank you!
My colleague helped me. My problem was: i used "UserService" in a method with #PostConstuct annotation, so its run before the mock happened.
I have this classes:
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Autowired
private EntityManager entityManager;
#Override
public UserDetails loadUserByUsername( String username) throws UsernameNotFoundException {
Optional<User> userOptional = userRepository.findUserByUsername(username);
User user = userOptional.orElseThrow(
()->new UsernameNotFoundException("Username not found in the database")
);
return new MyUserDetails(user);
}
public void saveUser(User user){
User newUser=new User();
newUser.setUsername(user.getUsername());
newUser.setPassword(passwordEncoder.encode(user.getPassword()));
newUser.setEnabled(true);
newUser.setRoles(List.of(entityManager.find(Role.class,1)
));
userRepository.save(newUser);
}
public void deleteUser(User user){
userRepository.delete(user);
}
public UserDetails getCurrentlyLoggedUser(Authentication authentication){
Object principal = authentication.getPrincipal();
if(principal instanceof UserDetails){
String username=((User) principal).getUsername();
UserDetails loggedUser = loadUserByUsername(username);
return loggedUser;
}
return null;
}}
#Controller
#RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
#Autowired
private final UserDetailsService userDetailsService;
#GetMapping("/orderlist")
public String showOrdeList(Model model, #AuthenticationPrincipal Authentication authentication){
userDetailsService.getCurrentlyLoggedUser
}
}
I want to know why the methods from UserDetailsServiceImpl class won't show up in the controller...
I can acces only the methods from the interface, but not the ones implemented besides them.
I tried to use qualifiers, and even made a config class to instantiate the impl class when injecting the bean by interface.
#Configuration
public class OrderConfig {
#Bean("userDetailsService")
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl();
}
Can anyone tell me what I'm doing wrong?
Methods defined in UserDetailsServiceImpl but not in UserDetailsService aren't accessible because you're injecting your service by interface UserDetailsService and compiler doesn't know which implementation will be injected in runtime.
So, you need to define your own interface with all methods that you want to expose or inject your service by class.
#Autowired
private final UserDetailsServiceImpl userDetailsService;
I would recommend a custom interface that extends UserDetailsService and injecting your service using your custom interface.
#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);
My spring application is already configured via xml stye.I am trying to change it with #Configuration classes.
My app is used another project as maven library.I have a service, annotated with #Named and this service is used by another service in the library.
#Named("userDetailsService")
public class UserDetailsServiceImpl extends AbstractServiceImpl implements UserDetailsService {
#Inject
private UserService userService;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userService.getByUserName(username);
}
}
#Named("userService")
public class UserServiceImpl extends BaseDaoServiceImpl<User, UserDao> implements UserService {
#Inject
private AuthorityService authorityService;
#Inject
private UserAuthorityService userAuthorityService;
#Override
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public User getByUserName(String username) {
return dao.getByUserName(username);
}
#Override
public List<User> getUserWithHasAuthority(String authorityName) {
return dao.getUserWithHasAuthority(authorityName);
}
#Override
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public User insert(User user) {
user.setEnabled(true);
super.insert(user);
Authority authority = authorityService.getByName("ROLE_USER");
UserAuthority userAuthority = new UserAuthority();
userAuthority.setAuthority(authority);
userAuthority.setUser(user);
userAuthorityService.insert(userAuthority);
return user;
}
}
On my new #Configuration class
#Configuration
#ComponentScan(basePackages = {"com.mylibrary","com.myapp"})
#EnableAspectJAutoProxy(proxyTargetClass = true)
#EnableTransactionManagement
public class ServiceTestConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
and this is my test method:
#Test
public void test() {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("admin", "admin");
authenticationManager.authenticate(authenticationToken);
}
it gives null pointer exception on userService property that exist in userDetailsService.Both of them #Named.
Thnx for your help
Test classes :
#ContextConfiguration(classes = {ServiceTestConfiguration.class, DataSourceConfiguration.class, SecurityConfiguration.class})
#Transactional
public class AbstractTest {
}
#RunWith(SpringJUnit4ClassRunner.class)
public class ServiceTest extends AbstractTest {
#Inject
private AuthenticationManager authenticationManager;
#Test
public void test() {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("admin", "admin");
authenticationManager.authenticate(authenticationToken);
}
}