ModelMapper throws NPE using JUnit Mockito - java

I'm having a NPE using ModelMapper
CatalogServiceTest
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class CatalogServiceTest {
#Rule
public ExpectedException thrown = ExpectedException.none();
#InjectMocks private CatalogService service;
#Mock ModelMapper modelMapper;
#Mock CatalogMapper catalogMapper;
#Mock CatalogRepository catalogRepository;
#Before
public void setUp() throws Exception {
// MockitoAnnotations.initMocks(this);
CatalogEntity catalogEntity = new CatalogEntity();
catalogEntity.setId("id");
catalogEntity.setCode("code");
catalogEntity.setType("type");
catalogEntity.setValue("value");
// Optional<CatalogEntity> optionalCatalog = Optional.of(catalogEntity);
when(catalogRepository.findByCode(any(String.class))).thenReturn(catalogEntity);
}
#Test
public void whenFindByCode() {
//Act
CatalogDto myCatalogDto = service.findByCode("code");
//Assert
assertTrue(myCatalogDto.getCode().equals("code"));
}
}
CatalogService
#Service
public class CatalogService {
private static final Logger LOGGER = LoggerFactory.getLogger(CatalogService.class);
#Autowired
CatalogRepository catalogRepository;
#Autowired
CatalogMapper catalogMapper;
/**
*
* #param type
* #return catalog objects which type is type
*/
public List<CatalogDto> findByType(String type) {
LOGGER.info("Getting catalogs by type {}", type);
List<CatalogEntity> catalogsEntityList = catalogRepository.findByType(type);
List<CatalogDto> catalogDtoList = new ArrayList<>();
catalogsEntityList.forEach(catalogEntity -> {
catalogDtoList.add(catalogMapper.convertCatalogEntityToCatalogDto(catalogEntity));
});
return catalogDtoList;
}
/**
* Find catalog by code.
* #param code
* #return catalog
*/
public CatalogDto findByCode(String code) {
LOGGER.info("Getting catalogs by code {}", code);
return catalogMapper.convertCatalogEntityToCatalogDto(catalogRepository.findByCode(code));
}
}
CatalogMapper
#Component
public class CatalogMapper {
#Autowired
private ModelMapper modelMapper;
/**
* Converts CatalogEntity object to CatalogDto object
* #param catalogEntity
* #return converted CatalogDto object
*/
public CatalogDto convertCatalogEntityToCatalogDto(CatalogEntity catalogEntity) {
return modelMapper.map(catalogEntity, CatalogDto.class);
}
}
CatalogRepository
#Repository
public interface CatalogRepository extends MongoRepository<CatalogEntity, String> {
List<CatalogEntity> findByType(String type);
CatalogEntity findByCode(String code);
}
The problem
The catalogRepository.findByCode(code) is returning a CatalogEntity object as expected and the problem comes after executing catalogMapper.convertCatalogEntityToCatalogDto(catalogRepository.findByCode(code)); that return null.
I'm using Intellij and this is the breakpoint just right before execute catalogMapper.convertCatalogEntityToCatalogDto function.

The catalogMapper is a mock with no stubbed methods.
There are a few ways in which you can fix your test:
Option 1: only test interaction with CatalogMapper
In this option, you stub a call to catalogMapper.convertCatalogEntityToCatalogDto. This is a thin unit test, you only test interactions with collaborating services.
As you said you want to test real implementation of mapper, there are two options:
Option 2: use SpringBootTest
In this option, you rely on SpringBootTest to set up your entire application context.
You need following changes:
use #Autowired instead of #InjectMock to get you object under test
use #MockBean instead of #Mock for repository. SpringBootTest ignores #Mock.
get rid of other mocks
as it creates entire application context, I would exclude this option, unless a full integration test is your goal (you started with #SpringBootTest in your code)
#SpringBootTest
public class CatalogServiceTest {
#Rule
public ExpectedException thrown = ExpectedException.none();
#Autowired
private CatalogService service;
#MockBean
CatalogRepository catalogRepository;
}
Option 3: construct services you need yourself
get rid of #SpringBootTest
mock only objects you want to mock - the repository
create real objects for other services
you may need to change field injection to constructor injection in your services, which is a good idea anyway
#Service
public class CatalogService {
final CatalogRepository catalogRepository;
final CatalogMapper catalogMapper;
#Autowired
public CatalogService(CatalogRepository catalogRepository, CatalogMapper catalogMapper) {
this.catalogRepository = catalogRepository;
this.catalogMapper = catalogMapper;
}
}
This approach creates only objects that are used by your test, not entire application context, so will likely result in leaner test that option 2.
#RunWith(MockitoJUnitRunner.class)
public class CatalogServiceTest {
#Rule
public ExpectedException thrown = ExpectedException.none();
private CatalogService service;
#Mock
CatalogRepository catalogRepository;
#Before
public void setUp() throws Exception {
var modelMapper = new ModelMapper();
var catalogMapper =new CatalogMapper(modelMapper);
service = new CatalogService(catalogRepository, catalogMapper);
CatalogEntity catalogEntity = new CatalogEntity();
catalogEntity.setId("id");
catalogEntity.setCode("code");
when(catalogRepository.findByCode(any(String.class))).thenReturn(catalogEntity);
}
}

Related

how can i insert advanced data in spring boot test?

I'm making test code in spring boot.
But, my test code doesn't save the data using #Before method.
If i request to '/v1/stay/, it return empty array...
Please can you explain what is wrong with my code?
Here is my test code.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class StayControllerTest {
#MockBean
private StayService stayService;
#Autowired
private MockMvc mockMvc;
// givenStay method is the method generating dummy data
#Before
public void before() {
stayService.save(givenStay1());
stayService.save(givenStay2());
stayService.save(givenStay3());
stayService.save(givenStay4());
stayService.save(givenStay5());
}
#Test
#Transactional
void showStayList() throws Exception {
List<StayReq> original = new ArrayList<>();
original.add(givenStay1());
original.add(givenStay2());
original.add(givenStay3());
original.add(givenStay4());
original.add(givenStay5());
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/v1/stay")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println(result.getResponse());
}
}
And below code blocks are my StayController and StayService
#RestController
#ApiV1
#RequiredArgsConstructor
public class StayController {
private final StayService stayService;
private final ApiService apiService;
#GetMapping("/stay")
public ResponseEntity<Response> stayList() {
return apiService.okResponse(stayService.getList());
}
}
#Service
#RequiredArgsConstructor
public class StayService {
private final StayRepository stayRepository;
private final RoomRepository roomRepository;
public List<StayRes> getList() {
return stayRepository.findAll().stream().map(StayRes::new).collect(Collectors.toList());
}
#Transactional
public void save(StayReq stayReq) {
stayRepository.save(stayReq.toEntity());
}
}
You injected a mock, not a 'real' service. If you want to use a 'real' service - you need to replace #MockBean annotation with #Autowired annotation.
Or alternatively - you can configure mock in the test method to return some predefined data.

How to mock factory method in service test with mockito

Hi I am trying to test service layer. I have already wrote tests for ConverterFactory. I think I need the mock dependency classes which ConverterServiceImpl using but Still I got NullPointerException
This is my service class
#Service
#RequiredArgsConstructor
public class ConverterServiceImpl implements ConverterService {
ConverterFactory factory = new ConverterFactory();
private final WebLinkRepository webLinkRepository;
private final DeepLinkRepository deepLinkRepository;
#Override
public DeepLinkResponse toDeepLink(WebLinkRequest webLinkRequest) {
WebLink webLink;
String url = webLinkRequest.getUrl();
Converter converter = factory.getConverter(url);
webLink = new WebLink();
webLink.setUrl(url);
String convertedUrl = converter.toDeepLink(url);
webLink.setConvertedUrl(convertedUrl);
webLinkRepository.save(webLink);
return new DeepLinkResponse(convertedUrl);
}
}
And this is the test
#RunWith(MockitoJUnitRunner.class)
public class ConverterServiceImplTest {
#InjectMocks
ConverterServiceImpl converterService;
#Mock
WebLinkRepository webLinkRepository;
#Mock
DeepLinkRepository deepLinkRepository;
#Mock
ConverterFactory converterFactory;
#Mock
ProductConverter productConverter;
#Mock
WebLinkRequest webLinkRequest;
#BeforeAll
void init(){
webLinkRequest.setUrl(WEBLINK_ONLY_PRODUCT);
}
#Test
public void toDeepLink_only_content_id() {
ConverterFactory converterFactory = mock(ConverterFactory.class);
when(converterFactory.getConverter(any())).thenReturn(productConverter);
DeepLinkResponse deepLinkResponse = converterService.toDeepLink(webLinkRequest);
assertEquals(deepLinkResponse.getUrl(),"ty://?Page=Product&ContentId=1925865");
}
}
This code throws error says. What am i doing wrong here?:
java.lang.NullPointerException
at com.example.converter.service.factory.ConverterFactory.getConverter(ConverterFactory.java:13)
You don't need to create a ConverterFactory converterFactory = mock(ConverterFactory.class) a second time in your test method, since you have already created such mock as a class field.
Besides, you did not inject the mock created in the test method into the class under test, whereas the mock, created as a field, was injected using #InjectMocks annotation.
So just remove ConverterFactory converterFactory = mock(ConverterFactory.class) from test method:
#RunWith(MockitoJUnitRunner.class)
public class ConverterServiceImplTest {
#InjectMocks
ConverterServiceImpl converterService;
#Mock
ConverterFactory converterFactory;
// other stuff
#Test
public void toDeepLink_only_content_id() {
when(converterFactory.getConverter(any())).thenReturn(productConverter);
// other stuff
converterService.toDeepLink(webLinkRequest);
}
}

Model Mapper works on Live Code but is not working during JUNITs

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
}
...

Injecting DozerBeanMapper with Mockito

I'm using Dozer in my Spring services. How to inject a DozerBeanMapper into a tested service using JUnit and Mockito?
My java class (if simplified) looks like:
#Service
public class UnicornService {
private final DozerBeanMapper dozer;
#Autowired
public UnicornService(DozerBeanMapper dozer) {
this.dozer = dozer;
}
public UnicornDto convert(Unicorn unicorn) {
return dozer.map(unicorn, UnicornDto.class);
}
}
A test class using JUnit 4 + Mockito + Hamcrest looks like:
import static com.shazam.shazamcrest.MatcherAssert.assertThat;
import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs;
#RunWith(MockitoJUnitRunner.class)
public class UnicornServiceTest {
#Mock
private DozerBeanMapper dozer;
#InjectMocks
private UnicornService unicornService;
#Test
public void testConvert() throws Exception {
final Unicorn original = ...
final UnicornDto expected = ...
// Execute the method being tested
final UnicornDto result = unicornService.convert(original);
// Validation
assertThat(result, sameBeanAs(expected));
}
}
The problem is that a mocked Dozer instance is not mapping objects as expected - by default, Mockito stubs return empty or null objects. And if I remove #Mock annotation from the test, it throws NPE!
Use #Spy annotation on DozerBeanMapper object. This will allow you to call all the normal methods of the object while still this object is managed by Mockito (as a mock) and injected into a tested service.
#RunWith(MockitoJUnitRunner.class)
public class UnicornServiceTest {
#Spy
private DozerBeanMapper dozer;
#InjectMocks
private UnicornService unicornService;
// ...
Another solution that I've found is to refactor your code. It seems less attractive to me, because it makes more harm then good, just in the sake of writing tests.
Use injection via setter in the service
#Service
public class UnicornService {
private DozerBeanMapper dozer;
#Autowired
public setDozer(DozerBeanMapper dozer) {
this.dozer = dozer;
}
public UnicornDto convert(Unicorn unicorn) {
return dozer.map(unicorn, UnicornDto.class);
}
}
Refactored test:
#RunWith(MockitoJUnitRunner.class)
public class UnicornServiceTest {
#InjectMocks
private UnicornService unicornService;
#Before
public void injectDozer() {
final DozerBeanMapper dozer = new DozerBeanMapper();
unicornService.setDozer(dozer);
}
// ...
You should not be relying on the mapper creating a proper object at all, just that the service calls the mapper and returns its result. The actual mapping should be tested in a unit test for the mapper. Ie
#RunWith(MockitoJUnitRunner.class)
public class UnicornServiceTest {
#Mock
private DozerBeanMapper dozer;
#InjectMocks
private UnicornService unicornService;
#Test
public void testConvert() throws Exception {
final Unicorn original = mock(Unicorn.class);
final UnicornDto expected = mock(UnicornDto.class);
when(dozer.map(original, UnicornDto.class)).thenReturn(expected);
// Execute the method being tested
final UnicornDto result = unicornService.convert(original);
// Validate that the call was delegated to the mapper
assertThat(result, is(expected));
}
}

Junit: testing service with private #Autowired fields

I have been doing Junit tests the past few weeks so my experience, being a junior programmer is fairly limited. After testing the easier service classes in the project, now I am stuck.
The problem is that I can't inject some private final someRepository into the constructor of the service class that I am testing, namely:
#RunWith(SpringRunner.class)
public class SomeServiceTest {
#Mock
private SomeRepository someRepository;
#InjectMocks
private SomeService someService;
#Test
public void testMyFunc() {
SomeOtherDto param = new SomeOtherDto();
param.setVar1(...);
param.setVar2(...);
Mockito.when(someRepository.getIt()).thenReturn(-1L);
Mockito.when(someService.myPrivBoolBuilder(param,-1L))
.thenReturn(new BooleanBuilder());
Pageable pageable = null;
Page<SomeDto> result = someService.myFunc(param, pageable);
assertEquals(expResult,
}
/* ... */
}
and the service I am testing:
#Service
#Transactional
public class SomeService implements someAbstractService {
private final CustomMapper customMapper
private final SomeRepository someRepository;
private final SomeOtherRepository someOtherRepository;
#Autowired
public SomeService(final CustomMapper customMapper, final SomeRepository someRepository,
final SomeOtherRepository someOtherRepository, etc)
{ /* ... */ }
public Page<SomeDto> myFunc(final SomeOtherDto param, final Pageable pageable) {
final BooleanBuilder predicate = myPrivBoolBuilder(param,
someOtherRepository.getId());
return someRepository.findAll(predicare, pageable).map(obj -> {
return customMapper.map(obj) });
}
public BooleanBuilder myPrivBoolBuilder(final SomeOtherDto param, final Long id) {
BooleanBuilder predicate = new BooleanBuilder();
final QSomeRepository qSomeRepository = QSomeRepository.someRepository;
final QSomeOtherRepository qSomeOtherRepository = QSomeOtherRepository.someOtherRepository();
predicate.and(qSomeRepository.someField.someOtherField.goe(param.getX()));
predicate.and(qSomeRepository.someField2.someOtherField2.isNotNull()));
predicate.and(qSomeOtherRepository.someField.someOtherField.id.eq(id()
.or(qSomeOtherRepository.someField.someOtherField.id.in(...))));
return predicate;
}
/* ... */
}
My problem is that when I run the test someOtherRepository.getId() return null with SpringRunner.class. When I run with MockitoJUnitRunner.class the someService constructor throws a constructor error: someRepository is NULL
I have tried multiple ways (tried #Spy, #MockBean, Mockito().doReturn... syntax, etc), but these are the two errors I get. I'm pretty sure it's a matter of using the Mocking framework correctly.
If you need other snippets or details, I will kindly offer them.
The reason is that Mockito tries to construct the object because of the #InjectMocks annotation.
SpringRunner is not necessary as this is not a spring test. If you really want a special runner, you can go for MockitoJUnitRunner.
You can simply initialize the Mockito annotations in your Before method and then create your service instance with the constructor providing the mocked dependencies.
public class SomeServiceTest {
#Mock
private SomeRepository someRepository;
private SomeService someService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
someService = new SomeService(someRepository);
}
/* ... */
}
You could use ReflectionTestUtil provided by Spring to inject a mock of SomeRepository from the test class.
eg;
ReflectionTestUtils.setField(someService, "someRepository", mock(SomeRepository.class));

Categories

Resources