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);
}
Related
I am using RestTemplate exchange HttpMethod.POST method to POST to an endpoint. In my test file I am testing for success of the POST method. However with my current tests I am getting 401 Unauthorized error when POST request is made. I need help to Mock the API while making POST request in test file
Here is my main file
#Component
public class DataTestRepo {
private final RestTemplate restTemplate;
private final String url;
private final AllBuilder headersBuilder;
public DataTestRepo(
#Qualifier(Oauth.BEAN_NAME) AllBuilder headersBuilder,
RestTemplate restTemplate, String url) {
this.headersBuilder = headersBuilder;
this.restTemplate = restTemplate;
this.url = url;
}
public ResponseEntity<String> postJson(Set<String> results) {
ResponseEntity<String> result = null;
try {
JSONObject jsonObject = new JSONObject(body);
HttpEntity<String> request = new HttpEntity<String>(jsonObject.toString(), null);
restTemplate.getMessageConverters().add(stringConvertor);
result = restTemplate.exchange(url, HttpMethod.POST,
new HttpEntity<>(request, getHttpHeaders()), String.class);
}
return result;
}
}
Here is my test file
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource
public class DataTestRepoTest {
private static final String url = "http://localhost:8080/data/name";
#Mock
private DataTestRepo DataTestRepo;
RestTemplate restTemplate = new RestTemplate();
#Test
public void restTemplateHttpPost_success() throws URISyntaxException {
URI uri = new URI(url);
Set<String> mockData = Stream.of("A","B").collect(Collectors.toSet());
Map<String, String> body = new HashMap<>();
body.put("Name", "Aws");
JSONObject jsonObject = new JSONObject(body);
HttpEntity<String> request = new HttpEntity<>(jsonObject.toString(), null);
ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.POST,
new HttpEntity<>(request, DataTestRepo.getHttpHeaders()), String.class);
Assert.assertEquals(201, result.getStatusCodeValue());
}
}
You are testing the logic inside DataTestRepo class, so you should not mock it.
RestTemplate is a dependency inside DataTestRepo, so this is exactly what you need to mock.
In general it should look like this inside your test:
#InjectMocks
private DataTestRepo DataTestRepo;
#Mock
RestTemplate restTemplate;
Also, you will have to provide a return value for your mocked dependency, like this:
Mockito.when(restTemplate.exchange(ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(new ResponseEntity<>(yourExpectedDataHere, HttpStatus.OK));
enter code here
This is just a simple example. A good practice would be to check that the arguments passed to your mock equal to the expected ones. One way would be to replace ArgumentMatchers.any() with the real expected data. Another is to verify it separately, like this:
Mockito.verify(restTemplate, Mockito.times(1)).exchange(ArgumentsMatchers.eq(yourExpectedDataHere), ArgumentsMatchers.eq(yourExpectedDataHere), ArgumentsMatchers.eq(yourExpectedDataHere), ArgumentsMatchers.eq(yourExpectedDataHere));
This is a great read on this topic: https://reflectoring.io/spring-boot-web-controller-test/
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);
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);
}
This is my call to be mocked:
ResponseEntity<Response> response = restTemplate.exchange(url, HttpMethod.POST, entity, Response.class);
mocking like below :
#Test
public void getSpecalityInventoryItemsCallPositiveTest(){
RestTemplateBuilder restTemplateBuilder = Mockito.mock(RestTemplateBuilder.class);
RestTemplate restTemplate = Mockito.mock(RestTemplate.class);
GetSpecalityInventoryItemsCaller getSpecalityInventoryItemsCaller = new GetSpecalityInventoryItemsCaller(restTemplateBuilder,id,pw);
Response inventoryItemsRespone = new Response();
inventoryItemsRespone.setStatusCode("0000");
BDDMockito.given(restTemplateBuilder.messageConverters(BDDMockito.any(ArrayList.class))).willReturn(restTemplateBuilder);
BDDMockito.given(restTemplateBuilder.additionalInterceptors(BDDMockito.any(ArrayList.class))).willReturn(restTemplateBuilder);
BDDMockito.given(restTemplateBuilder.build()).willReturn(restTemplate);
ResponseEntity<Response> responseEntity = new ResponseEntity<Response>(inventoryItemsRespone, HttpStatus.OK);
BDDMockito.given(restTemplate.exchange(url,HttpMethod.POST,HttpEntity.EMPTY,Response.class)).willReturn(responseEntity);
Response response = getSpecalityInventoryItemsCaller.getSpecalityInventoryItemsCaller(rxNumber);
Assertions.assertThat(response.getStatusCode()).isEqualTo("0000");
}
but getSpecalityInventoryItemsCaller.getSpecalityInventoryItemsCaller(rxNumber) is executed , inside restTemplate.exchange(url, HttpMethod.POST, entity, Response.class); call is returning null response
i need my own response Response
inventoryItemsRespone = new Response();
inventoryItemsRespone.setStatusCode("0000");
Not getting my own response , please help me out.
My guess is that the stubbing you are doing is matching on exact arguments. The problem is that the actual method may call may include arguments which have the same value as your stubbing but are not the same actual objects.
You should try using matchers for the restTemplate stubbing too:
BDDMockito.given(restTemplate.exchange(any(),any(),any(),any())).willReturn(responseEntity);
Although you might want to make it more precise than the any() matchers I have used here as an example, but it's worth testing to see if this works.
I am using RestTemplate to make an HTTP call to our service which returns a simple JSON response. I don't need to parse that JSON at all. I just need to return whatever I am getting back from that service.
So I am mapping that to String.class and returning the actual JSON response as a string.
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.getForObject(url, String.class);
return response;
Now the question is -
I am trying to extract HTTP Status codes after hitting the URL. How can I extract HTTP Status code from the above code? Do I need to make any change into that in the way I doing it currently?
Update:-
This is what I have tried and I am able to get the response back and status code as well. But do I always need to set HttpHeaders and Entity object like below I am doing it?
RestTemplate restTemplate = new RestTemplate();
//and do I need this JSON media type for my use case?
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
//set my entity
HttpEntity<Object> entity = new HttpEntity<Object>(headers);
ResponseEntity<String> out = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
System.out.println(out.getBody());
System.out.println(out.getStatusCode());
Couple of question - Do I need to have MediaType.APPLICATION_JSON as I am just making a call to url which returns a response back, it can return either JSON or XML or simple string.
Use the RestTemplate#exchange(..) methods that return a ResponseEntity. This gives you access to the status line and headers (and the body obviously).
getStatusCode()
getHeaders()
If you don´t want to leave the nice abstraction around RestTemplate.get/postForObject... methods behind like me and dislike to fiddle around with the boilerplate stuff needed when using RestTemplate.exchange... (Request- and ResponseEntity, HttpHeaders, etc), there´s another option to gain access to the HttpStatus codes.
Just surround the usual RestTemplate.get/postForObject... with a try/catch for org.springframework.web.client.HttpClientErrorException and org.springframework.web.client.HttpServerErrorException, like in this example:
try {
return restTemplate.postForObject("http://your.url.here", "YourRequestObjectForPostBodyHere", YourResponse.class);
} catch (HttpClientErrorException | HttpServerErrorException httpClientOrServerExc) {
if(HttpStatus.NOT_FOUND.equals(httpClientOrServerExc.getStatusCode())) {
// your handling of "NOT FOUND" here
// e.g. throw new RuntimeException("Your Error Message here", httpClientOrServerExc);
}
else {
// your handling of other errors here
}
The org.springframework.web.client.HttpServerErrorException is added here for the errors with a 50x.
Now you´re able to simple react to all the StatusCodes you want - except the appropriate one, that matches your HTTP method - like GET and 200, which won´t be handled as exception, as it is the matching one. But this should be straight forward, if you´re implementing/consuming RESTful services :)
If you want all the HTTPStatus from a RestTemplate including 4XX and 5XX, you will have to provide an ResponseErrorHandler to the restTemplate, since the default handler will throw an exception in case of 4XX or 5XX
We could do something like that :
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
#Override
public boolean hasError(HttpStatus statusCode) {
return false;
}
});
ResponseEntity<YourResponse> responseEntity =
restTemplate.getForEntity("http://your.url.here", YourResponse.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.XXXX);
private RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(url,HttpMethod.GET, requestEntity,String.class);
response contains 'body', 'headers' and 'statusCode'
to get statusCode : response.getStatusCode();
exchange(...) works but if you want less code, you can use
org.springframework.boot.test.web.client.TestRestTemplate.getForEntity(...)
which returns an Entity containing StatusCode. Change your example code to this:
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
HttpStatus statusCode = response.getStatusCode();
To test it you can use this snippet from my unit test:
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
assertResponseHeaderIsCorrect(response, HttpStatus.OK);
/**
* Test the basics of the response, non-null, status expected, etc...
*/
private void assertResponseHeaderIsCorrect(ResponseEntity<String> response, HttpStatus expectedStatus) {
assertThat(response).isNotNull();
assertThat(response.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON_UTF8);
assertThat(response.getStatusCode()).isEqualTo(expectedStatus);
}
There can be some slightly trickier use cases someone might fall in (as I did). Consider the following:
Supporting a Page object in order to use it with RestTemplate and ParameterizedTypeReference:
RestPageResponse:
import java.util.ArrayList;
import java.util.List;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
public class RestResponsePage<T> extends PageImpl<T>{
private static final long serialVersionUID = 3248189030448292002L;
public RestResponsePage(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public RestResponsePage(List<T> content) {
super(content);
}
public RestResponsePage() {
super(new ArrayList<T>());
}
}
Using ParameterizedTypeReference will yield the following:
ParameterizedTypeReference<RestResponsePage<MyObject>> responseType =
new ParameterizedTypeReference<RestResponsePage<MyObject>>() {};
HttpEntity<RestResponsePage<MyObject>> response = restTemplate.exchange(oauthUrl, HttpMethod.GET, entity, responseType);
Calling #exchange:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<?> entity = new HttpEntity<>(headers);
response = restTemplate.exchange("localhost:8080/example", HttpMethod.GET, entity, responseType);
Now here is the "tricky" part.
Trying to call exchange's getStatusCode will be impossible because the compiler, unfortunately, will be unaware of the "intended" type of response.
That is because generics are implemented via type erasure which removes all information regarding generic types during compilation (read more - source)
((ResponseEntity<RestResponsePage<MyObject>>) response).getStatusCode()
In this case, you have to explicitly cast the variable to the desired Class to get the statusCode (and/or other attributes)!
Putting this much of code is enough for me
HttpStatus statusCode = ((ResponseEntity<Object>) responseOfEsoft).getStatusCode();
You can use this solution
RestTemplate restTemplate = new RestTemplate();
final String baseUrl = "http://www.myexampleurl.com";
URI uri = new URI(baseUrl);
ResponseEntity<String> result = restTemplate.getForEntity(uri, String.class);
//get status code
int statuCode = result.getStatusCodeValue();
Was able to solve this through:
HttpEntity<Object> entity = restTemplate.getForEntity(uri, Object.class);
ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
System.out.println(result.getStatusCode());