Background
I have a simple SpringBoot application in which I am testing an UPDATE to my Domain Object from a DTO. Naturally - I am using a ModelMapper to convert from DTO->Entity. The issue I am running into is that while the ModelMapper is working perfectly in the live run, its not working during JUNITs. I put a breakpoint in the initBaseModelMapper
in my Configuration file during both JUNIT and LIVE runs and the breakpoint hits successfully. But in JUNITS, during the actual mapping - the null
values are still being applied to the Domain entity but not during the live run which works perfectly.
Configuration
@Configuration
public class ModelMapperConfiguration {
@Bean(name = "myEntityMapper")
public ModelMapper modelMapper() {
return initBaseModelMapper();
}
public static ModelMapper initBaseModelMapper() {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setPropertyCondition(Conditions.isNotNull());
modelMapper.getConfiguration().setSkipNullEnabled(true); // Tried without this as well
return modelMapper; // Gets hit during LIVE and JUNITS
}
}
Main Class Method Under Test
public class MyCaseService {
@Autowired
@Qualifier("myEntityMapper")
private ModelMapper modelMapper;
@Override
@Transactional
public @ResponseBody
MyCaseEntity updateMyCase(
@Valid final String myCaseId,
@Valid MyCaseDTO myCase) throws Exception {
MyCaseEntity existingEntity = entityRepository.find(myCaseId);
modelMapper.map(myCase, existingEntity);
return existingEntity;
}
JUNIT
I put a breakpoint the the ModelConfiguration
and I can see it getting Initialized exactly like when the code is running live. However, for some reason, the ModelMapper is IGNORING the skipping of null fields unlike when its running live
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes= {ModelMapperConfiguration.class})
public class MyCaseServiceTest {
@InjectMocks
private MyCaseService testSubject;
@Spy
@Qualifier("myEntityMapper")
private ModelMapper modelMapper;
@Before
public void setUp() {
// Initialized `testEntityCase` etc with Id etc
}
@Test
public void testUpdate() throws Exception {
Mockito.when(entityRepository.find(Mockito.any())).thenReturn(testEntityCase);
MyCaseEntity myCase = testSubject.updateMyCase(
"1",
testCaseDTO);
assertEquals(1L, myCase.getId().longValue()); // <- Test Fails with NullPointer. Id becomes null during JUNIT.
}
One way to overcome theses Problems is to autowire the constructur of MyCaseService instesd of the private member
public class MyCaseService {
private ModelMapper modelMapper;
@Autowired
MyCaserService(@Qualifier("myEntityMapper") ModelMapper modelMapper) {
this.modelMapper = modelMapper;
}
@Override
@Transactional
public @ResponseBody
MyCaseEntity updateMyCase(
@Valid final String myCaseId,
@Valid MyCaseDTO myCase) throws Exception {
MyCaseEntity existingEntity = entityRepository.find(myCaseId);
modelMapper.map(myCase, existingEntity);
return existingEntity;
}
}
In the Test you can use the Spy to create the Service
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes= {ModelMapperConfiguration.class})
public class MyCaseServiceTest {
@Spy
@Qualifier("myEntityMapper")
private ModelMapper modelMapper;
private MyCaseService testSubject;
@Before
public void setUp() {
testSubject = new MyCaseService(modelMapper);
// Initialized `testEntityCase` etc with Id etc
}
...
Thanks. This seems simple and logical - dont know why I didnt consider Constructor DI :) Appreciate it!!
Glad I could help!