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));
Related
I have a spring service (MyService) that uses a mapstruct mapper (CustomMapstructMapper) :
#Service
public class MyService {
private final ClassA classA;
private final ClassB classB;
/*
other private fields declarations...
*/
private CustomMapstructMapper customMapstructMapper = Mappers.getMapper(CustomMapstructMapper.class);
//MyService constructor
public MyService(final ClassA classA, etc...) {
this.classA = classA;
//etc...
}
public ServiceOutput mainMethod(some parameters) {
//some business logic
MyMapperOutput myMapperOutput = customMapstructMapper.map(MapperParameter parameter);
ServiceOutput serviceOutput = some business logic with myMapperOutput;
return serviceOutput;
}
}
I want to unit test MyService (I am using Junit 5) and mock my CustomMapstructMapper output without calling the real mapper during the test execution. I already have another test class that specificly test all the custom mappings in CustomMapstructMapper.
So I have this test class :
#ExtendWith(MockitoExtension.class)
class MyServiceTest {
#InjectMocks
private MyService myService;
#Mock
private CustomMapstructMapper customMapstructMapper;
#Mock
private MyMapperOutput myMapperOutput;
#Test
void testMyService() {
/*
Some mocks creation ..
*/
Mockito.when(myMapperOutput.getSomeField()).thenReturn("Some value");
Mockito.when(customMapstructMapper.map(Mockito.any(MapperParameter.class))).thenReturn(myMapperOutput);
ServiceOutput serviceOutput = myService.mainMethod(some parameters);
/*
Some assertions on serviceOutput
*/
}
}
When I run my test, the implementation of my mapper customMapstructMapperImpl is called in MyService, not my mock.
A NullPointerException is thrown in my mapper because some fields are not initiated.
I have tried to create a mock with the implementation of my mapstruct mapper :
#Mock private CustomMapstructMapperImpl customMapstructMapper;
but I get the same result.
What am I doing wrong?
You're not taking advantage of the spring framework and mapstruct support for it.
in your service change:
private CustomMapstructMapper customMapstructMapper = Mappers.getMapper(CustomMapstructMapper.class);
into
#Autowired
private CustomMapstructMapper customMapstructMapper;
If you don't have it yet at your mapper use
#Mapper( componentModel = "spring" )
This will cause the generated mapper to have the #Component annotation from the spring framework, and it becomes possible to auto-wire this.
After making these changes your method of supplying a mock for testing should work.
The code below works correctly. So maybe something else happened. Your code example is not quite complete.
#ExtendWith(MockitoExtension.class)
public class TestForService {
#InjectMocks
private Service service;
#Mock
private Mapper mapper;
#Test
public void test_service() {
System.out.println("test_service start");
Mockito.when(mapper.doSomeTh()).thenReturn("mock mapper doSomeTh");
String result = service.service();
System.out.println("test_service end, result: " + result);
}
static class Service {
private Mapper mapper = Mapper.getMapper();
public String service() {
System.out.println("Service.service");
return mapper.doSomeTh();
}
public void setMapper(Mapper mapper) {
this.mapper = mapper;
System.out.println("Service.setMapper: " + mapper);
}
}
static class Mapper {
public String doSomeTh() {
System.out.println("Mapper.doSomeTh");
return "mapper end";
}
public static Mapper getMapper() {
System.out.println("Mapper.getMapper");
return null;
}
}
}
result:
Mapper.getMapper
Service.setMapper: mapper
test_service start
Service.service
test_service end, result: mock mapper doSomeTh
A simpler way here:
Just add a package-private setter method into your MyService
// for testing only
void CustomMapstructMapper setCustomMapstructMapper(CustomMapstructMapper mapper) {
this.customMapstructMapper = mapper;
}
and inject your mocked CustomMapstructMapper in test code
#Test
void testMyService() {
/*
Some mocks creation ..
*/
Mockito.when(myMapperOutput.getSomeField()).thenReturn("Some value");
Mockito.when(customMapstructMapper.map(Mockito.any(MapperParameter.class))).thenReturn(myMapperOutput);
myService.setCustomMapstructMapper(customMapstructMapper); // inject your mocked customMapstructMapper here
ServiceOutput serviceOutput = myService.mainMethod(some parameters);
/*
Some assertions on serviceOutput
*/
}
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);
}
}
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);
}
}
I think there is either a basic misunderstanding on my part with how when works, or more specifically how Mockito is working.
I have a service class that has a utility class injected via constructor. The utility class has some other dependencies autowired by constructor as well.
A service class method calls several methods in the utility class. The test uses when/thenReturn statements on the called utility methods. When I make a call on the service method, I get an NPE on a utility method called with a null parameter. But I expected parameters set in the when clause to be set. Code below:
#Service
public class ServiceClass {
private Utility utility;
public ServiceClass(Utility utility) {
this.utility = utility;
}
public serviceMethod(MyDocument myDocument, List<Attachment> attachments) {
SomeType variable1;
OtherType variable2;
List<String> stringList;
long time;
time = utility.method1(variable1, variable2);
stringList = utility.method2(myDocument, attachments.get(0));
...
}
#Service
public class Utility {
private Dependency1 depend1;
private Dependency2 depend2;
public Utility(Dependency1 depend1, Dependency2 depend2) {
this.depend1 = depend1;
this.depend2 = depend2;
}
public long method1(SomeType var1, OtherType var2) {
....
}
public List<String> method2(MyDocument myDoc, Attachment attach) {
....
}
Now the test code looks as follows:
public TestClass {
private ServiceClass serviceClass;
#Mock
private Depend1 depend1;
#Mock
private Depend2 depend2;
#InjectMocks
private Utility utility;
#Rule
public MockitoRule rule = MockitoJUnit.rule();
#Before
public void setup() {
serviceClass = new ServiceClass(utility);
}
#Test
public testServiceMethod() {
long time = System.currentTimeMillis();
MyDocument doc = new MyDocument();
List<Attachments> attachments = Arrays.asList(new Attachment(...), new Attachment(...));
SomeType some = new SomeType();
OtherType other = new OtherType();
when(utility.method1(some, other)).thenReturn(time);
when(utility.method2(doc, attachments.get(0)).thenReturn(Arrays.asList(new String("stg 1"), new String("stg 2"));
String resp = serviceClass.serviceMethod(doc, attachments);
assertEquals("service completed", resp);
}
}
But when utility.method2 is called, myDocument shows as null. I was expecting that it would be an instance of MyDocument.
Do I have something misconfigured? Am I missing a concept here? All help appreciated!
Thanks.
UPDATE
Corrected the arguments to the serviceMethod.
The ServiceClass is the class you are testing, so you should anotate with #Mock only the dependencies of this class in your test, in your case the utility attribute, remove Depend1 and Depend1 declarations. The setup method is not necessary, you can anotate serviceClass in your test with #InjectMocks instead, it take cares of the injections automatically. And finally, your TestClass need the #RunWith(MockitoJunitRunner.class) to make everything work if it's not there.
#RunWith(MockitoJunitRunner.class)
public class TestClass{
#InjectMocks
private ServiceClass serviceClass;
#Mock
private Utility utility;
}
This is a basic definition for your TestClass, the test itself looks correct, but can be improved to use ArgumentMatchers on the "when" clause and add a verify clause using ArgumentCaptor to validate the parameters.
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));
}
}