'Mockito mocking a method within the mocked class

I am trying to mock a method in the below class, I am unable to do that and the code flow goes into the implemantion method and it leads to nullPointerException.

The class is as below and the method that, I intend to mock is getPredictionList(String, String).

    public class PredictionService {
    
        @Inject
        private ElasticSearch elasticSearch;

        private final RestHighLevelClient restHighLevelClient = OpenSearchRestHighLevelClient.getRestHighLevelClient();
        

        public String getPredictionList(String query, String index){
            
            try {
            //Method call successfully mocked
            SearchSourceBuilder searchSourceBuilder = elasticSearchEntity.createQuery(query);
            SearchRequest  searchRequest = new SearchRequest(index);
            searchRequest.source(searchSourceBuilder);
            
            //Method to be mocked
            SearchResponse serchResponse = getSearchResponse(searchRequest);
            
            //..code..//
            
            }catch(Exception e) {
                e.printStackTrace();
            }
            
            return 
        }
        
        
        public SearchResponse getSearchResponse(SearchRequest searchRequest){
            return restHighLevelClient.search(searchRequest, RequestOption.DEFAULT);
        }
    }
    

The corresponding Testing class is as below.

First, I have created a mock of PredictionService and then injected the dependencies. I have mocked getSearchResponse method using this statement.

 when(mockPredictionService.getSearchResponse(any())).thenReturn(searchResponse);

But when the code executes, this mock is not present and the code flows into the getSearchResponse implementation and throws null pointer exception.

    class PredictionServiceTest{
        
        //Write create a mock to test and inject the resources.
        
        @Spy
        @InjectMock
        PredictionService injectMockPredictionService;
        
        //Mock the resource 
        @Mock 
        ElasticSearchEntity mockElasticSearchEntity;
        
        @InjectMock
        PredictionService mockPredictionService;
        
        @Test
        public void getPredictionListTest() {
            
            //Mocking createQuery Response
            ElasticSearchEntity elasticSearchEntity = ElasticSearchEntity();
            SearchSourceBuilder searchSourceBuilder = elasticSearchEntity.createQuery("inputQuery");
            when(mockElasticSearchEntity.createQuery(any())).thenReturn(searchSourceBuilder);
            
            //Mocking searchResponse
            SearchResponse searchResponse = getDummySearchResponseImplementation();
            when(mockPredictionService.getSearchResponse(any())).thenReturn(searchResponse);
            
            String actualResponse = injectMockPredictionService.getPredictionList("inputQuery", "someIndex");       
        }
                    
    }

If I am to replace mockPredictionService with injectMockPredictionService it directly goes into the implementation method at this line itself and then returns null pointer exception.

when(mockPredictionService.getSearchResponse(any())).thenReturn(searchResponse);

replaced code.

when(injectMockPredictionService.getSearchResponse(any())).thenReturn(searchResponse);

Update

I have two mocks of PredictionService,

// To call the method
@Spy
@InjectMock
PredictionService injectMockPredictionService;


// To Provide mock for getSearchResponse Method
@InjectMock
PredictionService mockPredictionService;

As per the link I have changed the implementation to

@InjectMock
PredictionService injectMockPredictionService = Mockito.spy(new PredictionService ());

But this did not help, The code goes into the implementation class and throws NULL pointer exception. I have updated the question.



Solution 1:[1]

  1. Create a partial mock for PredictionService. You need to use Spy mock. When you use the spy then the real methods are called (unless a method was stubbed). So some methods will be executed, and others will be mocked.

  2. Second problem is that Spy annotation can not work together with InjectMocks. Use Mockito.spy instead.

 @org.mockito.InjectMocks
 PredictionService mockPredictionService = Mockito.spy(new PredictionService());

Another option is to create a constructor in PredictionService and inject mock manually:

PredictionService mockPredictionService = Mockito.spy(new PredictionService(mockElasticSearch));

Example of working test with comments:

import org.junit.runner.RunWith;
import org.mockito.Mockito;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@RunWith(org.mockito.junit.MockitoJUnitRunner.class)
public class PredictionServiceTest{
    @org.mockito.Mock
    ElasticSearch mockElasticSearch; //create mock for elastic search

    @org.mockito.InjectMocks
    PredictionService mockPredictionService = Mockito.spy(new PredictionService());// create partial mock for your service, because you mock only one method of real object
                                                                                   //inject mocks in your service

    @org.mockito.Mock
    SearchResponse searchResponse;//create a mock for the search response or you can create your own response without mock

    @org.junit.Test
    public void getPredictionListTest() {
        //perform mock of elastic search
        SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.searchSource();
        when(mockElasticSearch.createQuery(any())).thenReturn(searchSourceBuilder);

        //partially mock your service
        when(mockPredictionService.getSearchResponse(any())).thenReturn(searchResponse);

        //execute test
        String actualResponse = mockPredictionService.getPredictionList("inputQuery", "someIndex");

        //verify that mock was executed
        verify(mockPredictionService, times(1)).getSearchResponse(any());
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1