ResponseEntity<List<AgreementRecord>> myEntity = new
ResponseEntity<List<AgreementRecord>>(HttpStatus.ACCEPTED);
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.<HttpEntity<?>>any(),
ArgumentMatchers.<Class<?>>any())).thenReturn(myEntity);
The rest template returns a list from the application
Eclipse throws a compilation error
The method thenReturn(ResponseEntity) in the type OngoingStubbing>
is not applicable for the arguments
(ResponseEntity>)
Rest template
ResponseEntity<List<AgreementRecord>> responseEntity =
restTemplate.exchange(smoUrl+ GET_AGREEMENT_RECORDS + customerId
,HttpMethod.GET,null,new
ParameterizedTypeReference<List<AgreementRecord>>() {
});
responseEntity.getBody();
One thing you can do is to side-step Mockito's compile-time type checking with doReturn().
doReturn(myEntity)
.when(restTemplate)
.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class)
ArgumentMatchers.<HttpEntity<?>>any(),
ArgumentMatchers.<Class<?>>any()));
This will return myEntity without compile-time type check. if you've got the type wrong, you'll find out as soon as you run your test.
Edit:
#Test()
public void test() {
class AgreementRecord {}
ResponseEntity<List<AgreementRecord>> myEntity = new ResponseEntity<>(
Arrays.asList(new AgreementRecord()), HttpStatus.ACCEPTED);
doReturn(myEntity)
.when(restTemplate)
.exchange(
anyString(),
any(HttpMethod.class),
any(),
any(ParameterizedTypeReference.class));
ResponseEntity<List<AgreementRecord>> responseEntity = restTemplate.exchange(
"url", HttpMethod.GET, null, new ParameterizedTypeReference<List<AgreementRecord>>() {});
List<AgreementRecord> body = responseEntity.getBody();
assertTrue(responseEntity.getStatusCode().is2xxSuccessful());
assertNotNull(body);
}
Related
I have been using Mockito in this way for nearly a decade and I am at a loss what is going on here. What am I doing incorrectly here?
The actual call appears correct, but why is my stubbing shown as having empty String and nulls for parameters in the error trace?
Method under test:
private String harvestToken;
private String allUsersUrl;
#Autowired
private RestTemplate restTemplate;
public List<HarvestUser> getActiveHarvestUsers() {
Map<String, String> params = new HashMap<>();
params.put("is_active", "true");
String urlTemplate = UriComponentsBuilder.fromHttpUrl(allUsersUrl).queryParam("is_active", "true")
.toUriString();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
httpHeaders.add("Authorization", "Bearer " + harvestToken);
HttpEntity<Void> requestEntity = new HttpEntity<>(httpHeaders);
ResponseEntity<HarvestAllUsers> response = restTemplate.exchange(urlTemplate, HttpMethod.GET, requestEntity,
HarvestAllUsers.class, params);
HarvestAllUsers users = response.getBody();
if (users != null)
return users.getUsers();
else {
log.warn("Harvest users not found!");
return Collections.emptyList();
}
}
Test:
#Mock
RestTemplate mockRestTemplate;
#InjectMocks
HarvestRestClient harvestRestClient;
#Test
void testGetActiveHarvestUsers() {
ReflectionTestUtils.setField(harvestRestClient, "harvestToken", "abc123");
ReflectionTestUtils.setField(harvestRestClient, "allUsersUrl", "http://dummy_url.com");
when(mockRestTemplate.exchange(anyString(), eq(HttpMethod.GET), any(RequestEntity.class),
any(Class.class), any(Map.class))).thenReturn(new ResponseEntity<HarvestAllUsers>(new HarvestAllUsers(), HttpStatus.OK));
List<HarvestUser> result = harvestRestClient.getActiveHarvestUsers();
assertNotNull(result);
}
Note: any(), anyString() and eq() are imported from org.mockito.ArgumentMatchers.*
I also tried using the Mockito.* versions to same effect.
Error trace:
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'exchange' method:
mockRestTemplate.exchange(
"http://dummy_url.com?is_active=true",
GET,
<[Content-Type:"application/json", Authorization:"Bearer abc123"]>,
class com.demo.dto.harvest.response.HarvestAllUsers,
{"is_active" = "true"}
);
-> at com.demo.client.HarvestRestClient.getActiveHarvestUsers(HarvestRestClient.java:68)
- has following stubbing(s) with different arguments:
1. mockRestTemplate.exchange(
"",
null,
null,
null,
null
);
-> at com.demo.client.HarvestRestClientTest.testGetActiveHarvestUsers(HarvestRestClientTest.java:52)
Typically, stubbing argument mismatch indicates user mistake when writing tests.
Mockito fails early so that you can debug potential problem easily.
Update:
I was able to get the test to pass by changing the stubbing as follows:
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), ArgumentMatchers.<RequestEntity<Void>>any(), any(Class.class),
any(Map.class))).thenReturn(new ResponseEntity<HarvestAllUsers>(new HarvestAllUsers(), HttpStatus.OK));
But I am keeping this question up because I still have no idea why the original test was failing, and this seems like an unnecessary change.
Say,
I have a method getTemplateData() and I want to mock a restTemplate.exchange() method call in that
public List<String> getTemplateData(String templateId, String storeId, String projectId) {
RestTemplate restTemplate = new RestTemplate();
Map<String, String> bodyObject = new HashMap<>();
bodyObject.put("functionalAreaId", templateId);
bodyObject.put("storeId", storeId);
bodyObject.put("projectId", projectId);
HttpEntity<?> requestEntity = new HttpEntity<>(bodyObject, null);
ResponseEntity<List<String>> result =
restTemplate.exchange(
BaseUrl + "/template",
HttpMethod.POST,
requestEntity,
new ParameterizedTypeReference<List<String>>() {});
List<String> screens = result.getBody().stream().collect(Collectors.toList());
log.info(
"Completed executing the method getTemplateData for the templateId:{} and storeId:{}",
templateId,
storeId);
return screens;
}
I want to mock the line
ResponseEntity<List<String>> result =
restTemplate.exchange(
BaseUrl + "/template",
HttpMethod.POST,
requestEntity,
new ParameterizedTypeReference<List<String>>() {});
Is it possible to mock a statement in Java?
Yes, that can be mocked.
#Test
public void test() {
// let's create mock first, you can use annotation also
RestTemplate mockTemplate = Mockito.mock(RestTemplate.class);
Mockito.when(mockTemplate.exchange(Mockito.eq(BaseUrl + "/template"),
Mockito.eq(HttpMethod.POST),
Mockito.eq(requestEntity),
Mockito.any(ParameterizedTypeReference.class))
.thenReturn(new ResponseEntity<>(Arrays.asList("data"), HttpStatus.OK))
}
I have a method in which is it using RestTemplate. I using the following code to make a call:
final ResponseEntity<RESTResponse> responseEntity = restTemplate.exchange(uri,
HttpMethod.POST,
httpEntityWithHeaders,
RESTResponse.class);
httpEntityWithHeads is of type HttpEntity<String>. I am writing a test and trying to mock RestTemplate so that when it calls the exchange method, it will throw an exception.
I am trying to mock it like this:
when(restTemplate.exchange(
ArgumentMatchers.contains(randomHost),
ArgumentMatchers.eq(HttpMethod.POST),
ArgumentMatchers.<HttpEntity<List<String>>>any(),
ArgumentMatchers.<ParameterizedTypeReference<List<RESTResponse>>>any())
).thenThrow(new ResourceAccessException("Random exception message."));
But when running the test, it doesn't throw the exception, it just continues.
Any suggestions?
As you said httpEntityWithHeads is of type HttpEntity<String>, so you have to stub in way that matches to HttpEntity<String>
when(restTemplate.exchange(
ArgumentMatchers.contains(randomHost),
ArgumentMatchers.eq(HttpMethod.POST),
ArgumentMatchers.<HttpEntity<String>>any(),
ArgumentMatchers.<ParameterizedTypeReference<List<RESTResponse>>>any())
).thenThrow(new ResourceAccessException("Random exception message."));
To me seems that your last parameter is not a list it is a class, and that is why the stub is failing, I tried the following and it is working.
#Test(expected = IllegalArgumentException.class)
public void test() {
RestTemplate restTemplate = mock(RestTemplate.class);
when(restTemplate.exchange(anyString(), ArgumentMatchers.eq(HttpMethod.POST),
any(HttpEntity.class),
any(Class.class))).thenThrow(new IllegalArgumentException("a"));
Rest rest = new Rest(restTemplate);
rest.call();
}
public void call(){
HttpEntity<Object> httpEntityWithHeaders= new HttpEntity<>(null);
final ResponseEntity<Object> responseEntity = restTemplate.exchange("a",
HttpMethod.POST,
httpEntityWithHeaders,
Object.class);
}
Relevant Code Below:
ServiceCode:
#Override
public ResponseEntity<AppointmentResponse> createAppointment(AppointmentRequest partnerFulfillmentRequest) {
RestTemplate rt = null;
ResponseEntity<AppointmentResponse> response = null;
String uri = null;
HttpEntity<AppointmentRequest> httpEntity = null;
HttpHeaders headers = null;
try {
rt = new RestTemplate();
rt.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
rt.getMessageConverters().add(new StringHttpMessageConverter());
uri = new String(internalServiceUrl+"/"+APP_NAME_INTERNAL+"/appointment");
log.info("Calling internal service URL : "+uri);
headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
httpEntity = new HttpEntity<AppointmentRequest>(partnerFulfillmentRequest, headers);
response = rt.exchange(uri, HttpMethod.PUT, httpEntity, AppointmentResponse.class);
if (response != null)
{
log.info("Got response from internal servicec-->statusCode: "+response.getStatusCodeValue());
log.info("Got response from internal service--> Body "+response.getBody());
}
}catch(HttpClientErrorException hceEx) {
//hceEx.printStackTrace();
AppointmentResponse res = new AppointmentResponse();
return new ResponseEntity<AppointmentResponse>(mapResponse(hceEx.getResponseBodyAsString()), hceEx.getResponseHeaders(), hceEx.getStatusCode());
}catch(Exception e) {
e.printStackTrace();
AppointmentResponse res = new AppointmentResponse();
ResponseEntity<AppointmentResponse> wfmErrResponse = new ResponseEntity<AppointmentResponse>(res, HttpStatus.INTERNAL_SERVER_ERROR);
log.error("ERROR WHILE CALLING INTERNAL SERVICE");
log.error(uri);
log.error(e);
return wfmErrResponse;
}
return response;
}
Test Code:
#RunWith(MockitoJUnitRunner.class)
public class PartnerFulfillmentServiceImplTest {
#Mock
RestTemplate restTemplate;
#Mock
HttpHeaders httpHeaders;
#Mock
ResponseEntity responseEntity;
#InjectMocks
PartnerFulfillmentServiceImpl partnerFulfillmentService;
#Test
public void createAppointmentTest() {
Whitebox.setInternalState(partnerFulfillmentService, "internalServiceUrl", "http://localhost:8080");
AppointmentRequest appointmentRequest = new AppointmentRequest();
appointmentRequest.setPartnerName("CENTRICITY");
appointmentRequest.setTicketNumber("123ABC");
httpHeaders = new HttpHeaders();
httpHeaders.set("Content-type", "application/json");
responseEntity = new ResponseEntity<>(
"some response body",
HttpStatus.OK
);
when(restTemplate.exchange(Mockito.anyString(),
Mockito.<HttpMethod> any(),
Mockito.<HttpEntity<?>> any(),
Mockito.<Class<Object>> any()))
.thenReturn(responseEntity);
ResponseEntity<AppointmentResponse> response = partnerFulfillmentService.createAppointment(appointmentRequest);
Assert.assertEquals(response.getStatusCode(), HttpStatus.OK);
}
}
I'm getting
java.lang.AssertionError:
Expected :500
Actual :200
and understandably so because it is not actually calling running .thenReturn(responseEntity); logic. My million dollar question is, why? It should be returning the responseEntity value. I have all arguments for the exchange() to any() in hopes to trigger the condition as often as possible as I can always narrow the conditions at a different time. Am I not mocking my restTemplate correctly? That is my current suspicion as to what is going on. Any advice would help!
Thanks!
Like #JB Nizet pointed out, you are creating a new instance of RestTemplate inside your tested method. This means that the exchange method will be called from the new instance, and not a mock. You could implement it the way you did if the class that contains the method createAppointment had a dependency injection of RestTemplate.
What you want there, is to mock the constructor of the new instance of RestTemplate so that, when a new instance would be created, it will be substituted. Unfortunately, Mockito is not capable of mocking a constructor, so you should use PowerMockito for mocking constructors.
whenNew(RestTemplate.class).withNoArguments().thenReturn(restTemplate);
responseEntity = new ResponseEntity<>(
"some response body",
HttpStatus.OK
);
when(restTemplate.exchange(Mockito.anyString(),
Mockito.<HttpMethod> any(),
Mockito.<HttpEntity<?>> any(),
Mockito.<Class<Object>> any()))
.thenReturn(responseEntity);
In my request handler I want to do some validation and based on the outcome of validation check I will return different response (success/error). So I create a abstract class for the response object and make 2 subclasses for failure case and successful case. The code looks something like this, but it doesn't compile, complaining that errorResponse and successResponse cannot be converted to AbstractResponse.
I'm quite new to Java Generic and Spring MVC so I don't know of a simple way to solve this.
#ResponseBody ResponseEntity<AbstractResponse> createUser(#RequestBody String requestBody) {
if(!valid(requestBody) {
ErrorResponse errResponse = new ErrorResponse();
//populate with error information
return new ResponseEntity<> (errResponse, HTTPStatus.BAD_REQUEST);
}
createUser();
CreateUserSuccessResponse successResponse = new CreateUserSuccessResponse();
// populate with more info
return new ResponseEntity<> (successResponse, HTTPSatus.OK);
}
There are two problems here:-
Your return type has to be changed to match the two response subclasses ResponseEntity<? extends AbstractResponse>
When you instantiate your ResponseEntity you cannot use the simplified <> syntax you have to specify which response class you are going to use new ResponseEntity<ErrorResponse> (errResponse, HTTPStatus.BAD_REQUEST);
#ResponseBody ResponseEntity<? extends AbstractResponse> createUser(#RequestBody String requestBody) {
if(!valid(requestBody) {
ErrorResponse errResponse = new ErrorResponse();
//populate with error information
return new ResponseEntity<ErrorResponse> (errResponse, HTTPStatus.BAD_REQUEST);
}
createUser();
CreateUserSuccessResponse successResponse = new CreateUserSuccessResponse();
// populate with more info
return new ResponseEntity<CreateUserSuccessResponse> (successResponse, HTTPStatus.OK);
}
Another approach would be using error handlers
#ResponseBody ResponseEntity<CreateUserSuccessResponse> createUser(#RequestBody String requestBody) throws UserCreationException {
if(!valid(requestBody) {
throw new UserCreationException(/* ... */)
}
createUser();
CreateUserSuccessResponse successResponse = new CreateUserSuccessResponse();
// populate with more info
return new ResponseEntity<CreateUserSuccessResponse> (successResponse, HTTPSatus.OK);
}
public static class UserCreationException extends Exception {
// define error information here
}
#ExceptionHandler(UserCreationException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ErrorResponse handle(UserCreationException e) {
ErrorResponse errResponse = new ErrorResponse();
//populate with error information from the exception
return errResponse;
}
This approach enables the possibility of returning any kind of object, so an abstract super class for the success case and the error case (or even cases) is no longer necessary.