User27854
User27854

Reputation: 884

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.

Upvotes: 1

Views: 4593

Answers (2)

Steven Dewey
Steven Dewey

Reputation: 61

I stumbled on this thread while trying to set up some mocks for a slightly more complicated situation and figured I'd share my results for posterity.

My situation was similar in the fact that I needed to mock dependencies, but I also wanted to mock some of the methods on the class I was testing. This was the solution:

    @MockBean
    DependentService mockDependentService
    
    ControllerToTest controllerToTest

    @BeforeEach
    public void setup() {
       mockDependentService = mock(DependentService.class);
       controllerToTest = mock(ControllerToTest.class);
       ReflectionTestUtils.setField(controllerToTest, "dependantService", mockDependentService);
    }
    
    @Test
    void test() {
        //set up test and other mocks 
        //be sure to implement the below code that will call the real method that you are wanting to test
        when(controllerToTest.methodToTest()).thenCallRealMethod();
        //assertions
    }

Note that "dependantService" needs to match whatever you have named the instance of the service on your controller. If that doesn't match the reflection will not find it and inject the mock for you.

This approach allows all the methods on the controller to be mocked by default, then you can specifically call out which method you want to use the real one. Then use the reflection to set any dependencies needed with the respective mock objects.

Upvotes: 1

Eugene
Eugene

Reputation: 6005

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

Upvotes: 1

Related Questions