Search code examples
javaspring-bootjunitmockitomodelmapper

map() method from mocked ModelMapper returns null value


i'm struggling with some weird problem related with Mockito and JUnit 5 with Spring Boot application.

Here is the fragment of my AccountServiceTest class:

@ExtendWith(MockitoExtension.class)
@Import(MapperConfig.class)
class AccountServiceTest {

    @Mock
    private AccountRepository accountRepository;

    @Mock
    private ModelMapper modelMapper;

    @Mock
    private PasswordEncoder passwordEncoder;

    @Mock
    private AuthenticationManager authenticationManager;

    @Mock
    private JwtTokenUtil jwtTokenUtil;

    @InjectMocks
    private AccountService underTest;

    @Test
    void itShouldCreateAccountAndAddItToDatabase() {
        // given
        CreateAccountDto dto = new CreateAccountDto("service.tester@test.com", "service.tester", "12345");

        // when
        Account account = underTest.create(dto);
        Account found = accountRepository.findByUsername(account.getUsername()).orElseThrow();

        // then
        assertEquals(found, account);
    }
    ...
}

Here is the service I want to test:

@Service
public class AccountService implements FieldValueExists {

    @Autowired
    private AccountRepository accountRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private ModelMapper modelMapper;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    public Account create(CreateAccountDto dto) {
        Account account = modelMapper.map(dto, Account.class);
        account.setHashedPassword(passwordEncoder.encode(dto.getPassword()));

        accountRepository.save(account);

        return account;
    }
    ...
}

Everything seems to work in the AccountService itself when used in the controller, but in the testing unit the modelMapper.map(...) method returns null object, so it throws an infamous NullPointerException.

Also I've managed to put a Configuration for this mapper in MapperConfig:

@Configuration
public class MapperConfig {

    @Bean
    public ModelMapper modelMapper() {
        ModelMapper modelMapper = new ModelMapper();

        /*
          Account's related mappings
         */
        modelMapper
                .typeMap(CreateAccountDto.class, Account.class);

        modelMapper
                .typeMap(Account.class, AccountDto.class);

        modelMapper
                .typeMap(Account.class, AuthorDto.class);

        /*
          Note's related mappings
         */
        modelMapper
                .typeMap(CreateNoteDto.class, Note.class);

        modelMapper
                .typeMap(Note.class, NoteDto.class);

        modelMapper
                .typeMap(EditNoteDto.class, Note.class)
                .addMappings(mapper -> {
                    mapper.when(ctx -> ctx.getSource() != null).map(EditNoteDto::getTitle, Note::setTitle);
                    mapper.when(ctx -> ctx.getSource() != null).map(EditNoteDto::getContent, Note::setContent);
                });


        return modelMapper;
    }
}

I don't know what is going wrong, since the dependencies in AccountServiceTest are mocked correctly, I think.


Solution

  • Actually, it is required to mock your own implementation to every method participating in service we want to test.

    For example using BDDMockito:

    given(modelMapper.map(dto, Account.class))
        .willReturn(/*some object we want to return*/)
    

    It is the only thing we need in our Unit Test, because we only want to check the logic of something we're testing.

    For testing every part of our service we must write Integration Test.

    Yes, it was fault, and I want to thank @Turing85 for pointing me to the solution of the problem.