Issue
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("[email protected]", "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.
Answered By - TenDan
Answer Checked By - Mildred Charles (JavaFixing Admin)