I am starting with Mockito to do unit tests. I have seen several examples of how a unit test can be carried out but it does not work for me since when I pass the object to search to check if it exists and modify it, the exception is thrown and the test is interrupted.
When this line is executed: BDDMockito.given(syndromeTypeServiceMock.modifySyndromeType(synTypeMock)).willReturn(synTypeMockEspect); It enters the method in the Service and when validating with the mock, by not going to the database to look for the record, it returns null or empty and the following validation raises the exception. How can I validate this exception with Mockito so that it passes the test?
Thansk in advance!
@ExtendWith(MockitoExtension.class)
public class SyndromeTypeServiceTest {
Logger LOGGER = LoggerFactory.getLogger(getClass());
@Mock
SyndromeTypeRepository syndromeTypeRepository;
@Mock
SyndromeTypeTransRepository syndromeTypeTransRepository;
@InjectMocks
SyndromeTypeServiceImpl syndromeTypeServiceMock;
@Test
public void test_modify_SyndromeType_when_synTransId_exist() throws PersistenceException {
LOGGER.info("test_modify_SyndromeType_when_synTransId_exist >>");
SyndromeType synTypeMock = SyndromeType.builder()
.synTypeId(10)
.synTypeName("Musculoesquelético.")
.lanCode("es")
.synTypeTransId(10)
.build();
SyndromeType synTypeMockEspect = SyndromeType.builder()
.synTypeId(10)
.synTypeName("Musculoesquelético.")
.lanCode("es")
.synTypeTransId(10)
.build();
BDDMockito.given(syndromeTypeServiceMock.modifySyndromeType(synTypeMock)).willReturn(synTypeMockEspect);
assertEquals(synTypeMock.getSynTypeTransId(), synTypeMockEspect.getSynTypeTransId());
assertEquals(synTypeMock.getSynTypeId(), synTypeMockEspect.getSynTypeId());
assertEquals(synTypeMock.getSynTypeName(), synTypeMockEspect.getSynTypeName());
LOGGER.info("<< test_modify_SyndromeType_when_synTransId_exist");
}
}
Service layer
@Service
@Transactional
public class SyndromeTypeServiceImpl implements SyndromeTypeService {
Logger LOGGER = LoggerFactory.getLogger(getClass());
final SyndromeTypeRepository syndromeTypeRepository;
final SyndromeTypeTransRepository syndromeTypeTransRepository;
public SyndromeTypeServiceImpl(SyndromeTypeRepository syndromeTypeRepository,
SyndromeTypeTransRepository syndromeTypeTransRepository) {
this.syndromeTypeRepository = syndromeTypeRepository;
this.syndromeTypeTransRepository = syndromeTypeTransRepository;
}
@Override
public List<SyndromeType> renderSyndromeTypes(List<SyndromeTypeTransEntity> synTypesEtts){
LOGGER.info("Rendering SyndromeTypeEntity results");
List<SyndromeType> synTypes = new ArrayList<SyndromeType>(0);
if(null != synTypesEtts) {
synTypesEtts.stream()
.forEach(
s -> {
synTypes.add(
SyndromeType.builder()
.synTypeId(s.getSynTypeEtt().getSynTypeId())
.synTypeName(s.getSynTypeName())
.lanCode(s.getLanCodeEtt().getLanCode())
.synTypeTransId(s.getSynTypeTransId())
.build()
);
});
}
return synTypes;
}
@Override
public SyndromeType modifySyndromeType(SyndromeType synType) throws PersistenceException {
LOGGER.info("modifySyndromeType >>");
Optional<SyndromeTypeTransEntity> synTypeEtt = java.util.Optional.empty();
if(null != synType.getSynTypeTransId()) {
synTypeEtt = syndromeTypeTransRepository.findById(synType.getSynTypeTransId());
}
if(!synTypeEtt.isPresent() || synTypeEtt.isEmpty()) {
throw new PersistenceException(ErrorMessage.RECORD_NOT_EXIST.name());
}
SyndromeTypeTransEntity synTypeTrans = SyndromeTypeTransEntity.builder()
.lanCodeEtt(LanguageEntity.builder()
.lanCode(synType.getLanCode())
.build())
.synTypeName(synType.getSynTypeName())
.build();
synTypeTrans = syndromeTypeTransRepository.save(synTypeTrans);
List<SyndromeType> synTypesTO = this.renderSyndromeTypes(List.of(synTypeTrans));
SyndromeType synTypeTO = synTypesTO.isEmpty() ? null : synTypesTO.get(0);
LOGGER.info("<< modifySyndromeType");
return synTypeTO;
}
}
Mockito mock ojects (created with Mockito.mock
or @Mock
) by default always return null
or empty collections. If you require them to return a different value, you have to stub the methods.
@InjectMocks
on the other hand creates a real instance of your interface/class and injects the mock objects as dependencies. A real instance's methods cannot be stubbed – and it wouldn't make sense really, because this is the class that you usually want to exercise in your test.
This means that if you want to test your service, you need to make sure that you are calling the real service and that any methods it calls return a useful value.
The following block calls findById
on the repository (not the service), for which you have created a mock object, but never stubbed any methods.
if(null != synType.getSynTypeTransId()) {
synTypeEtt = syndromeTypeTransRepository.findById(synType.getSynTypeTransId());
}
The repository's method can be stubbed as follows in your test:
when(syndromeTypeTransRepository.findById(10)) // the id passed to the service
.thenReturn(Optional.of(...)); // a value that makes sense for your test
The second call to the repository must be stubbed as well:
synTypeTrans = syndromeTypeTransRepository.save(synTypeTrans);
A sufficient stub implementation is probably to simply return the argument itself:
when(syndromeTypeTransRepository.save(any()))
.thenReturn(AdditionalAnswers.returnsFirstArg());
After you have set up the stubbed calls in your test method, you must then call the real method of your class (if you are not calling the real method, then what are you testing!?)
final SyndromeType actualType = syndromeTypeServiceMock.modifySyndromeType(synType);
And then assert the correct value being returned:
assertEquals(expectedType, actualType); // if SynType implement equals()
or
// without equals() properties need to be compared manually
assertEquals(10, actualType.getSynTypeTransId());
assertEquals(10, actualType.getSynTypeId());
assertEquals("Musculoesquelético.", actualType.getSynTypeName());