JUnit test. The problem when converting entity to DTO by using ModelMapper library

Viewed 9093

I am working on the Spring Boot web app and I have a custom realization of the ModelMapper library that allows me to convert single objects and a list of objects.

@Component
public class ObjectMapperUtils {

@Autowired
private static ModelMapper modelMapper;

static {
    modelMapper = new ModelMapper();
    modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
}

private ObjectMapperUtils() {
}

public <D, T> D map(final T entity, Class<D> outClass) {
    return modelMapper.map(entity, outClass);
}

public <D, T> List<D> mapAll(final Collection<T> entityList, Class<D> outCLass) {
    return entityList.stream().map(entity -> map(entity, outCLass)).collect(Collectors.toList());
}
}

On the Service layer, I have a method returns from DB UserEntity object and convert it to UserDTO.

@Autowired
private UserRepository userRepository;

@Autowired
private ObjectMapperUtils modelMapper;

@Override
public UserDTO getByUserId(String userId) {
    UserEntity userEntity = userRepository.findByUserId(userId)
            .orElseThrow(() -> new NotFoundException("User with userId[" + userId + "] not found"));
    //UserDTO userDTO = new UserDTO();
    //BeanUtils.copyProperties(userEntity, userDTO);
    return modelMapper.map(userEntity, UserDTO.class); // userDTO;
}

The problem occurs when I try to create a test for this method. UserDTO always returned as NULL value.

class UserServiceImplTest {

@InjectMocks
private UserServiceImpl userService;

@Mock
private UserRepository userRepository;

@Mock
private ObjectMapperUtils modelMapper;

@BeforeEach
void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

@Test
void testGetByUserId() {
    UserEntity userEntity = new UserEntity();
    userEntity.setId(1L);
    userEntity.setUsername("zavada");
    userEntity.setUserId("33b4c069-e907-45a9-8d49-2042044c56e0");

    when(userRepository.findByUserId(anyString()))
                 .thenReturn(Optional.of(userEntity));

    UserDTO userDTO = userService.getByUserId("33b4c069-e907-45a9-8d49-2042044c56e0");
    System.out.println(userDTO); <--- NULL

    assertEquals("zavada", userDTO.getUsername());
    assertNotNull(userDTO);

}
}

When I use on the Service layer converting by BeanUtils.copyProperties(obj1, obj2); - the test is passed successfully. With ModelMapper I get NULL. Any ideas how to solve this error or refactor code? Thanks in advance

3 Answers

To build upon user268396 answer you would need the following to get this to work:

@RunWith(MockitoJUnitRunner.class)
    public class StackOverflowTest {

        @InjectMocks
        private StackOverflow userService = new StackOverflow();

        @Mock
        private UserRepository userRepository;

        @Mock
        private ObjectMapperUtils modelMapper;
        private UserDTO userDTO = new UserDTO();
        private UserEntity userEntity = new UserEntity();

        @Before
        public void setUp() {
            when(modelMapper.map(any(), any())).thenReturn(userDTO);

            userDTO.setId(1L);
            userDTO.setUsername("zavada");
            userDTO.setUserId("33b4c069-e907-45a9-8d49-2042044c56e0");
        }

        @Test
        public void testGetByUserId() throws Throwable {
            when(userRepository.findByUserId(anyString())).thenReturn(Optional.of(userEntity));

            UserDTO result = userService.getByUserId("33b4c069-e907-45a9-8d49-2042044c56e0");
            System.out.println(result);

            assertEquals("zavada", result.getUsername());
            assertNotNull(result);

        }
    }

This is quite an easy mistake to make, it is important to remember that all you @mocked objects are not real implementations anymore and if you expect any behaviour back you would need to define it upfront.

If you have a @Mock private ObjectMapperUtils modelMapper; then by default you do not have a real ObjectMapperUtils. So you are not calling your implementation, you are calling the default stubs that Mockito provides for you. That is why you are getting a null return value from modelMapper.map().

Either do not mock the ObjectMapperUtils bean or arrange for it to do something suitable using when() etc. from the Mockito API.

For the same situation, you could also do

  1. Do not mock the ObjectMapperUtils class in your test class.
  2. In the @Before init() ..{} method instantiate your UserServiceImpl to new instance of ObjectMapperUtils. And then set it in the UserServiceImpl. i.e , userServiceImpl.setMapper(UserServiceImpl).

So your setup method will look something like this ,

@InjectMocks
private UserServiceImple userServiceImpl;

@Mock
private UserRepository userRepository;

private ObjectMapperUtil objectMapperUitl; // not mocked

@BeforeEach
public void init() { 
    objectMapperUitls = new ObjectMapperUtils();
    userServiceImple.setMapper(objectMapperUitls);
}
Related