Reputation: 884
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
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
Reputation: 6005
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.
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