Mockito when any java.lang.NullPointerException - elasticsearch

I have this method which must be tested:
private void processRequest() {
BulkRequest request = new BulkRequest();
request.add(new IndexRequest("posts").id("1")
.source(XContentType.JSON,"field", "foo"));
request.add(new IndexRequest("posts").id("2")
.source(XContentType.JSON,"field", "bar"));
final BulkResponse bulkResponse = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
}
This is what I'm trying to do from my test class:
RestHighLevelClient restHighLevelClientMock = mock(RestHighLevelClient.class);
final String errorMessage = "error message";
final Exception cause = new Exception("test exception");
final boolean isFailed = true;
final int itemID = 0;
// define the item failure
BulkItemResponse.Failure failure = mock(BulkItemResponse.Failure.class);
when(failure.getCause()).thenReturn(cause);
when(failure.getMessage()).thenReturn(errorMessage);
// define the item level response
BulkItemResponse itemResponse = mock(BulkItemResponse.class);
when(itemResponse.isFailed()).thenReturn(isFailed);
when(itemResponse.getItemId()).thenReturn(itemID);
when(itemResponse.getFailure()).thenReturn(failure);
when(itemResponse.getFailureMessage()).thenReturn("error message");
List<BulkItemResponse> itemsResponses = Collections.singletonList(itemResponse);
// define the bulk response to indicate failure
BulkResponse response = mock(BulkResponse.class);
when(response.iterator()).thenReturn(itemsResponses.iterator());
when(response.hasFailures()).thenReturn(isFailed);
// have the client return the mock response
when(restHighLevelClientMock.bulk(any(BulkRequest.class), RequestOptions.DEFAULT)).thenReturn(response);
I'm getting java.lang.NullPointerException in this line:
when(restHighLevelClientMock.bulk(any(BulkRequest.class), RequestOptions.DEFAULT)).thenReturn(response);
Any idea why this happens? Thanks

I ran into this problem too, which led me to this github request:
https://github.com/elastic/elasticsearch/issues/40534
Elasticsearc's RestHighLevelClient class marked many of the public methods as final, making it impossible to mock.
There is a workaround detailed in the github page about creating a delegate, which is less than ideal but works.
EDIT: after digging around with possible solutions I found this article: see https://www.baeldung.com/mockito-final. I tried it in my own project and got my tests working with junit jupiter.
Add the following to your src/test/resources folder:
mockito-extensions/org.mockito.plugins.MockMaker
add the following line to the org.mockito.plugins.MockMaker file:
mock-maker-inline

Related

Spring SQS Message Handler - add custom message attributes to existing message before sending it to Dead-Letter-Queue

Currently, the messages on source queue are sent to dead-letter-queue on every exception. My goal is to add custom attributes to the failed message so that engineers have more information about failure when they monitor the dead-letter-queue.
When I tried to add attributes to existing message, I receive an java.lang.UnsupportedOperationException exception.
Here is the aspect code where I add message attributes to existing message. I am using custom aspect which is triggered before sending the message with AmazonSQSClient.
#Before(value = "execution(* com.amazonaws.services.sqs.AmazonSQS*Client.sendMessage*(com.amazonaws.services.sqs.model.SendMessageRequest,..)) && args(request,..)",
argNames = "request")
public void before(SendMessageRequest request) {
Map<String, MessageAttributeValue> messageAttributes = request.getMessageAttributes();
if (messageAttributes == null) return;
messageAttributes.put("MoveToDlqFlag", createMessageAttribute("true"));
}
This is the code where exception happens.
#SqsListener(value = {"${sqs.queue.source-queue}"}, deletionPolicy = ON_SUCCESS)
public void handleMessageReceived(String rawMessage, #Header("SenderId") String senderId, #Headers Map<String, Object> header) {
var orderMessageWrapper = messageWrapperUtil.create(rawMessage, Order.class);
var order = orderMessageWrapper.getMessage();
var receiveCount = (Integer) header.get("ApproximateReceiveCount");
...
}
Is there a way to add message attributes to existing message before sending to dead-letter-queue? Maybe spring provides a configuration where it is possible.
If messageAttributes map throws java.lang.UnsupportedOperationException, then maybe you could try to create a new mutable map, which is a copy of immutable map:
Map<String, MessageAttributeValue> messageAttributes = new HashMap<>(request.getMessageAttributes());
and then you can use setMessageAttributes() of SendMessageRequest
So I hope that this solution would work (unless setMessageAttributes doesn't throw UnsupportedOperationException too)
Map<String, MessageAttributeValue> messageAttributes = request.getMessageAttributes() == null ? new HashMap<>() : new HashMap<>(request.getMessageAttributes());
messageAttributes.put("MoveToDlqFlag", createMessageAttribute("true"));
request.setMessageAttributes(messageAttributes);

How to mock controller parameter method with Mockito?

I have the following controller method where criterias is an object build with query parameters :
#GetMapping
public List<Employee> findAll(CustomCriteria criterias) {
// this method build a custom mongoDB query object
final Query query = criterias.buildMongoDBQueryFromCriteria();
return employeeService.find(query);
}
The test is written as follow :
#Test
void get_all_employees_with_criterias() {
given(employeeService.find(any(Query.class)))
.willReturn(List.of(new Employee(), new Employee));
final var responseBody = mvc.perform(get("/employees?companyId=12,14")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn()
.getResponse().getContentAsString();
assertThatJson(responseBody).isArray().size().isEqualTo(2);
}
I can't find how to mock criterias.buildMongoDBQueryFromCriteria(). Indeed, there are a lot of logic inside this method, and I don't want it to be called for real with #WebMvcTest.
I have already tried to use #Spy annotation in the controller test class but it doesn't seems to work :/
I'm pretty sure that it must be really basic, but I didn't find any equivalent needs over Google.
Thanks for your help
EDIT
Based on #nnhthuan response I updated my test as follow, but it still doesn't work:
#Test
void get_all_employees_with_criterias() {
var customCriteriaMock = Mockito.mock(CustomCriteria.class);
var mockedQuery = Mockito.mock(Query.class);
when(customCriteriaMock.buildMongoDBQueryFromCriteria()).thenReturn(mockedQuery);
given(employeeService.find(mockedQuery))
.willReturn(List.of(new Employee(), new Employee()));
final var responseBody = mvc.perform(get("/employees?companyId=12,14")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andReturn()
.getResponse().getContentAsString();
assertThatJson(responseBody).isArray().size().isEqualTo(2);
}
If you're asking how to write unit test, and this is how, not what you're trying to write above.
#Test
public void testFindAll() {
CustomCriteria mockCriteria = Mockito.mock(CustomCriteria.class);
Query mockQuery = Mockito.mock(Query.class);
List<Employee> expectation = new ArrayList<>();
when(mockCriteria.buildMongoDBQueryFromCriteria()).thenReturn(mockQuery);
when(employeeService.find(mockQuery)).thenReturn(expectaion)
List<Employee> actual = controller.findAll(mockCriteria);
assertThat(actual).sameInstance(expectation); // Or whatever framework you are using to assert.
}
If you're asking how to write integration test with your real service, so do not use mocking framework. :)

mockMvc jUnit testing with #SortDefault and #PageableDefault

I am trying to test a pagination method which also sorts the content by created date. The method does work how I expect it, however I an unsure how I can correctly test it. Have found no working solution for my problem even the
I know that the issue is caused by #SortDefault being present as when I remove it the problem is solved, could work around it by creating an sql repository call which will sort it asc or desc.
Furthermore the error I currently receive is java.lang.AssertionError: Content type not set
tried adding .setCustomArgumentResolvers(new SortHandlerMethodArgumentResolver()).build(); but without any luck also tried other methods of .andExpect(content().contentTypeCompatibleWith("application/json")) also no luck. Still complains that content type is not set.
Below you can see my controller method
#GetMapping("/stuff")
public ResponseEntity<Page<Stuff>> findAllStuffs(#PageableDefault(value = 20) #SortDefault(sort = "created", direction = Sort.Direction.DESC)Pageable pageable){
log.info("Start of findAllStuffs method");
Page<Stuff> thePage=repository.findAll(pageable);
if(thePage==null )
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
else
return new ResponseEntity<>(thePage,HttpStatus.OK);
}
Below you can see my test controller method
#Test
public void findAllStuffs() throws Exception {
PageRequest pageRequest = new PageRequest(0,20);
Page<Stuff> pages = new PageImpl<>(stuffs);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).setCustomArgumentResolvers(new SortHandlerMethodArgumentResolver()).build();
when(repository.findAll(pageRequest)).thenReturn(pages);
this.mockMvc.perform(get("/api/stuffs")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$.content").isArray())
.andExpect(jsonPath("$.content", Matchers.hasSize(4)))
.andExpect(status().isOk());
}
Any help or suggestions will be truly appreciated.

Example for spring MVC DeferredResult with long running task

Can anyone tell me, how to do long running transactional task using spring DeferredResult ? went through a lot of tutorial available on net but neither documentation nor examples clear on non Rest based application, which doesn't need long polling but running the task at background and immediately returning the HTTP response and subsequent calls to the same controller method just return the result. With some assumption i have created like the following
private static final Map<String, DeferredResult<ModelAndView>> deferredResults = new ConcurrentHashMap<>();
#RequestMapping(value = "longRunning", method = RequestMethod.POST)
public DeferredResult<ModelAndView> longRunning(#ModelAttribute LongRunningJob longRunningJob) {
String resultKey = longRunningJob.getKey();
DeferredResult<ModelAndView> result = deferredResults.get(resultKey);
if (result == null ) {
deferredResults.put(resultKey, result = new DeferredResult<ModelAndView>());
new Thread(runLongRunning(longRunningJob, result)).start();
}
result.onCompletion(() -> {
deferredResults.remove(resultKey);});
return result;
}
public Runnable runLongRunning((LongRunningJob newLongRunningJob, DeferredResult deferredResult) {
return () -> {
LongRunningJob returnJobValue = this.longRunningJobService.startLongRunningJob(newLongRunningJob); //startLongRunningJob is a transactional method
ModelMap modelMap = new ModelMap();
modelMap.put("returnJobValue", returnJobValue);
modelMap.put("message", "Success");
deferredResult.setResult(new ModelAndView("job-view", modelMap));
};
}
Will it work or is there any other better way to handle it ? Will it be threadsafe is there any chances of getting into a race condition ?

Spring jaxb WebServiceGatewaySupport implementation java.lang.IllegalArgumentException

I'm working in jaxb with Spring, trying to write a custom unmarshalling process using WebServiceGatewaySupport.
My class is below. The problem is with response, when I call the following method
getWebServiceTemplate().sendSourceAndReceiveToResult
It crashes with message "java.lang.IllegalArgumentException: 'uri' must not be empty". It seems like even though I am using StringResult, it is trying to parse xml and finding a xml/soap response error.
public class WUResultGateway extends WebServiceGatewaySupport{
private WebServiceTemplate webServiceTemplate;
private SourceExtractor ratingResponseExtractor = new WUResponseExtractor();
public WUResultGateway(WebServiceTemplate webServiceTemplate){
this.webServiceTemplate = webServiceTemplate;
}
private Source marshall( SendRDCResults results ) throws IOException{
StringResult resp = new StringResult();
Marshaller marshaller = webServiceTemplate.getMarshaller();
marshaller.marshal( results, resp );
return new ResourceSource( new ByteArrayResource( resp.toString().getBytes() ) );
}
public Object wuResponse( SendRDCResults results) throws IOException{
//StringSource source = new StringSource();
Result result = new StreamResult();
StringResult strResult = new StringResult();
boolean flag = getWebServiceTemplate().sendSourceAndReceiveToResult( marshall( results ), strResult );
return result;
}
}
Without making any change to the response from the server, I want to get values in s String or simple xml format without errors. Can anyone help?
setDefaultUri(webServiceTemplate.getDefaultUri());
finally looks as follows
public Object wuResponse( SendRDCResults results) throws IOException{
//StringSource source = new StringSource();
Result result = new StreamResult();
StringResult strResult = new StringResult();
setDefaultUri(webServiceTemplate.getDefaultUri());
boolean flag = getWebServiceTemplate().sendSourceAndReceiveToResult( marshall( results ), strResult );
return result;
}

Resources