How to mock factory method in service test with mockito - java

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);
}
}

Related

How to Test void Method with spring repository using Junit and Mockito

i have a java spring service that call a spring data repository and i want to do a Junit test with mockito , this is my class and the service that i want to test :
#Service
public class DataServiceImpl implements DataService{
#Autowired
private CautionRepository cautionRepository;
#Override
public void addCautions(List<CautionsDTO> cautions, Contrat contrat) {
if(cautions != null && !cautions.isEmpty()) {
cautions.forEach(caution -> {
Caution caution = new Caution();
cautionContrat.setAmount(caution.getAmount());
cautionContrat.setDate(caution.getDate());
caution.setContrat(contrat);
cautionRepository.save(caution);
});
}
}
}
and this is my Unit test
#RunWith(SpringRunner.class)
public class DataServiceImplTest{
#InjectMocks
private DataServiceImpl dataService;
#Mock
private CautionRepository cautionRepository;
#Test
public void addListCautionsTest() {
List<CautionsDTO> cautions = new ArrayList<>();
ContratExportation contrat = new ContratExportation();
Caution caution = new Caution();
dataDelService.addCautions(cautions,contrat);
Mockito.verify(cautionRepository, times(1)).save(caution);
}
}
When i run the test i got the folowwing error :
Wanted but not invoked:
cautionRepository.save(
org.model.Caution#2abe9173
);
-> at org.service.DataServiceImplTest.addListCautionsTest(DataServiceImplTest.java:292)
Actually, there were zero interactions with this mock.
Do you have any idea please what is the mistake with my test
You never add a value to cautions so the loop is not executed and verify must fail.
Add a value to the list and the test should pass:
List<CautionsDTO> cautions = new ArrayList<>();
ContratExportation contrat = new ContratExportation();
CautionDTO caution = new CautionDTO();
cautions.add(caution);
dataDelService.addCautions(cautions,contrat);
This should be the correct code for executing the test
#RunWith(SpringRunner.class)
public class DataServiceImplTest{
#Autowired
private DataServiceImpl dataService;
#MockBean
private CautionRepository cautionRepository;
#Test
public void addListCautionsTest() {
List<CautionsDTO> cautions = new ArrayList<>();
ContratExportation contrat = new ContratExportation();
Caution caution = new Caution();
dataDelService.addCautions(cautions,contrat);
Mockito.verify(cautionRepository, times(1)).save(caution);
}
}
First of all, you forgot to add caution object into cautions list. But other than that you are mixing Unit test with Integration test.
You need to annotate your test class with #RunWith(MockitoJUnitRunner.class),
or
You need to annotate your mock objects with #MockBean and add #Autowired to your test class DataServiceImpl object.
Now, let me explain to you.
Unit Test
When you want to write a unit test, you should not make use of application context (autowiring).
By the way, a better approach is to annotate your DataServiceImpl with #RequiredArgsConstructor from Lombok and remove #Autowired from CautionRepository. This will allow you to instantiate DataServiceImpl in a setup method in your unit test.
Your DataServiceImpl class should be:
#Service
#RequiredArgsConstructor
public class DataServiceImpl implements DataService{
private final CautionRepository cautionRepository;
#Override
public void addCautions(List<CautionsDTO> cautions, Contrat contrat) {
// your code...
}
}
and your new unit test class:
#RunWith(MockitoJUnitRunner.class)
public class DataServiceImplTest{
private DataServiceImpl dataService;
#Mock
private CautionRepository cautionRepository;
#Before
public void setup() {
dataService = new DataServiceImpl(cautionsRepository);
}
#Test
public void addListCautionsTest() {
// your test code...
}
}
Integration Test
Now, if you want to create an integration test, use #RunWith(SpringRunner.class). By doing this your application context will be loaded. In your case you can create a mocked bean inside your context by annotating your object with #MockBean. This will inject mocked object into your context and it will get auto wired in your real class.
For this your new DataServiceImpl class can remain same as above. But change your integration test into:
#RunWith(SpringRunner.class)
public class DataServiceImplTest{
#Autowired
private DataServiceImpl dataService;
#MockBean // it will be injected automatically
private CautionRepository cautionRepository;
#Test
public void addListCautionsTest() {
// your test code...
}
}
Hope, you now understand the difference and the mistake you were doing :)

How to mock a member in the Class that you spy with powermockito

How can I mock a member class in another class which has already been spied by PowerMockito.spy()?
#Component
public class BoxFileDao {
#Autowired
private BoxFileService boxFileService;
public void uploadFile() {
.....
boxFileService.uploadFile(user, credential);
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest(BoxFileDao.class)
public class BoxFileDaoTest {
#Test
public void testUploadFile() {
BoxFileDao mock = PowerMockito.spy(new BoxFileDao());
(how do I get the boxFileService from mock?)
mock.uploadFile();
verify(boxFileService).uploadFile(user, credential);
}
}
You can use #InjectMock to inject the mocked boxFileService object in the real boxFileDao object. Your test class can be written something like
#RunWith(PowerMockRunner.class)
public class BoxFileDaoTest {
#Mock
private BoxFileService boxFileService;
#InjectMocks
private BoxFileDao boxFileDao;
#Test
public void testUploadFile() {
boxFileDao.uploadFile();
verify(boxFileService).uploadFile(user, credential);
}
}
First you create your class under test BoxFileDao while injecting the mock for boxFileService into it. Afterwards you can create the spy on it.
For example:
BoxFileDao dao = new BoxFileDao();
dao.boxFileService = Mockito.mock(BoxFileService.class);
BoxFileDao spy = Mockito.spy(dao);
But the question would be why do you even want to do that? Is there a reason to spy on BoxFileDao, your class under test?

Use Mocked object in JUnit / Mockito test

I have a JUnit test that reads
public class EventHandlerTest {
#Mock
ThreadPoolExtendedExecutor threadPoolExtendedExecutor;
private EventHandler handler;
private Map<Queue<SenderTask>> subBuffers = new HashMap<>();
#Before
public void setUp() {
// PROBLEM: threadPoolExtendedExecutor null!
handler = new EventHandler(subBuffers, threadPoolExtendedExecutor);
}
}
When I call new in setUp, I have threadPoolExtendedExecutor=null.
I would like to insert some mocked threadPoolExtendedExecutor so, I do not have NullPointer problems when calling its methods (so simple interface mock is enough for me at this moment)
You can simply mock it using (in setUp)
threadPoolExtendedExecutor = mock(ThreadPoolExtendedExecutor.class);
#Before
public void setUp() {
threadPoolExtendedExecutor = mock(ThreadPoolExtendedExecutor.class);
handler = new EventHandler(subBuffers, threadPoolExtendedExecutor);
}
You can also let MockitoJUnitRunner do it for you :
don't forget to inject mocks in your service under test by annotating it with #InjectMocks
#RunWith(MockitoJUnitRunner.class)
public class EventHandlerTest {
#Mock
ThreadPoolExtendedExecutor threadPoolExtendedExecutor;
If you would like to use the #Mock or #InjectMocks annotations on the test class fields then you need to add #RunWith(MockitoJUnitRunner.class) at the class level.
#RunWith(MockitoJUnitRunner.class)
public class EventHandlerTest {
#Mock
ThreadPoolExtendedExecutor threadPoolExtendedExecutor;
Another approach is to not use the above annotations and manually create mocks by calling org.mockito.Mockito.mock().

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));
}
}

Using #Mock and #InjectMocks

I'm currently studying the Mockito framework and I've created several test cases using Mockito.
But then I read that instead of invoking mock(SomeClass.class) I can use the #Mock and the #InjectMocks - The only thing I need to do is to annotate my test class with #RunWith(MockitoJUnitRunner.class) or use the MockitoAnnotations.initMocks(this); in the #Before method.
But it doesn't work - It seems that the #Mock won't work!
Here is my 2 codes revisions - one using the annotations and one without.
What am I doing wrong?
public class ReportServiceImplTestMockito {
private TaskService mockTaskService; // This is the Mock object
private ReportServiceImpl service;
#Before
public void init(){
service = new ReportServiceImpl();
mockTaskService = mock(TaskServiceImpl.class);
service.setTaskServiceImpl(mockTaskService);
}
/// ...
Some tests
}
As I said - this work great.
But the following wont:
#RunWith(MockitoJUnitRunner.class)
public class ReportServiceImplTestMockito {
#Mock
private TaskService mockTaskService;
#InjectMocks
private ReportServiceImpl service;
// Some tests
}
And here is the ReportServiceImpl class:
#Service
public class ReportServiceImpl implements ReportService {
#Autowired
private TaskService taskServiceImpl;
public ReportServiceImpl(){}
public ReportServiceImpl(TaskService taskService){
this.taskServiceImpl = taskService;
}
public void setTaskServiceImpl(TaskService taskServiceImpl) {
this.taskServiceImpl = taskServiceImpl;
}
}
What am I missing?
O.K, I got my mistake!!!
I've used the #InjectMocks but initialized the same variable in the init() method...
So what happened was that mockito injected the mock objects to my variable - but seconds later I ran it over - initializing that very same variable!!!
Your code works fine for me using Mockito 1.9.
Using an 1.8+ version of Mockito I get a very specific error message telling me exactly how to fix the problem. As php-coder suggests: For Mockito 1.8+ you need to initialize the field.
Did you see this or any other error message?
Edit:
The following code works for me. Small changes:
Removed Spring annotations
Removed Interface
Added Getter
Added empty TaskService
Added test with System.out.println
Does it produce an error for you? :
Service:
public class ReportServiceImpl {
private TaskService taskServiceImpl;
public ReportServiceImpl() {
}
public ReportServiceImpl(TaskService taskService) {
this.taskServiceImpl = taskService;
}
public void setTaskServiceImpl(TaskService taskServiceImpl) {
this.taskServiceImpl = taskServiceImpl;
}
public TaskService getTaskServiceImpl() {
return taskServiceImpl;
}
}
Dependency:
public class TaskService {
}
Test, prints mockTaskService:
#RunWith(MockitoJUnitRunner.class)
public class ReportServiceImplTestMockito {
#Mock
private TaskService mockTaskService;
#InjectMocks
private ReportServiceImpl service;
#Test
public void testMockInjected() {
System.out.println(service.getTaskServiceImpl());
}
}
I'm not sure, but try to create new instance of ReportServiceImpl manually (as you did in working example):
#InjectMocks
private ReportServiceImpl service = new ReportServiceImpl();

Categories

Resources