My service class code is as below:
public class MyServiceImpl implements MegatillAccessService {
#Autowired
RestTemplate restTemplate;
#Value("${api.key}")
private String apiKey;
#Value("${customers.url}")
private String postUrl;
#Override
public String pushCustomerData(List<Customer> listOfcustomers, String storeId) throws MyServiceException {
Set<Customer> setOfCustomers = new HashSet<>(listOfcustomers);
int noOfCustomersLoadedSuccessfully =0;
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("apiKey", apiKey);
headers.add("Content-Type", "application/json");
headers.add("storeId", storeId);
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
for(Customer customer: setOfCustomers){
HttpEntity<Customer> request = new HttpEntity<Customer>(customer, headers);
CustomerDataDto customerDataDto = null;
try {
customerDataDto = restTemplate.exchange(postUrl, HttpMethod.POST, request, CustomerDataDto.class).getBody();
}
catch (HttpClientErrorException ex) {
if (ex.getStatusCode().equals(HttpStatus.NOT_FOUND)) {
log.error("The customers service is not available to load data: "+ ex.getResponseBodyAsString(), ex);
throw new MyServiceException("The customers service is not available to load data",new RuntimeException(ex));
}
else{
log.warn("Error for customer with alias: "+customer.getAlias() +" with message: "+ ex.getResponseBodyAsString(), ex);
if(!ex.getResponseBodyAsString().contains("already found for this shop")){
throw new MyServiceException("An error occurred while calling the customers service with status code "+ex.getStatusCode(),new RuntimeException(ex));
}
}
}
catch(Exception e){
throw new MyServiceException("An error occurred while calling the customers service: ",new RuntimeException(e));
}
if(null != customerDataDto) {
noOfCustomersLoadedSuccessfully++;
log.debug("--------Data posted successfully for: ---------"+customerDataDto.getAlias());
}
}
String messageToReturn = "No. of unique customers from source: "+setOfCustomers.size()+". No. of customers loaded to destination without error: "+noOfCustomersLoadedSuccessfully;
return messageToReturn;
}
}
My Test class is as below:
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
public class MyServiceTest {
#InjectMocks
private MyService myService = new MyServiceImpl();
#Mock
RestTemplate restTemplate;
#Before
public void setUp() throws Exception
{
MockitoAnnotations.initMocks(this);
initliaizeModel();
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
}
#Test
public void pushAllRecords(){
Mockito.when(restTemplate.exchange(Matchers.anyString(), Matchers.any(HttpMethod.class), Matchers.<HttpEntity<?>> any(), Matchers.<Class<CustomerDataDto>> any()).getBody()).thenReturn(customerDataDto);
/*Mockito.when(restTemplate.exchange(Mockito.anyString(),
Mockito.<HttpMethod> eq(HttpMethod.POST),
Matchers.<HttpEntity<?>> any(),
Mockito.<Class<CustomerDataDto>> any()).getBody()).thenReturn(customerDataDto);*/
String resultReturned = myService.pushCustomerData(customers,"1235");
assertEquals(resultReturned, "No. of unique customers from source: 2. No. of customers loaded to destination without error: 2");
}
}
While running the test, I am getting NullPointerException at the line where I am giving the Mockito.when and thenReturn condition. I tried many combinations but it is still giving NPE. I can't even reach the method invocation.Can you please let me know where am I going wrong?
You get NullPointerException because you are doing too much in your Mockito.when. Your code inside when (shorter version):
restTemplate.exchange(args).getBody()
You are trying to mock getBody() but it is called on exchange(args). And what does exchange(args) returns? Mockito doesn't know what it should return and you didn't specify that so by default it returns null.
That's why you get NPE.
To fix this you can either do mocking step by step, ie.
ResponseEntity re = Mockito.when(exchange.getBody()).thenReturn(customerDataDto);
Mockito.when(restTemplate.exchange()).thenReturn(re);
or specify mock to return deep stubs, like that (if you want to use annotations):
#Mock(answer = Answers.RETURNS_DEEP_STUBS)
RestTemplate restTemplate;
Related
Mocking RestTemplate.exchange() is not working. The Response of restTemplate.exchange() mocking gives null value at BDS Adapter class. My test case is failing with null pointer exception in BDSAdapter class. (response.getStatusCodeValue() gives null pointer exception..Mockito hints
Unused... -> at com..policydetails_adapters.BDSAdapterTest.getInsuranceHoldings(BDSAdapterTest.java:56)
[MockitoHint] ...args ok? -> at com.policydetails_adapters.BDSAdapter.fetchInsuranceDetails(BDSAdapter.java:77)
Below are my classes.
Test Class:
#RunWith(MockitoJUnitRunner.class)
public class BDSAdapterTest {
#InjectMocks
private BDSAdapter bdsAdapter;
#Mock
private BDSFetchInsuranceDetailsRequest bdsFetchInsuranceDetailsRequest;
#Mock
private RestTemplate restTemplate;
#Mock
Environment env;
#Test
public void getInsuranceHoldings() throws InsuranceHoldingsException {
Mockito.when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<BDSFetchInsuranceDetailsResponse>>any()))
.thenReturn(sampleBDSCustomerInsuranceHoldings());
BDSFetchInsuranceDetailsResponse bdsFetchInsuranceDetailsResponse = bdsAdapter.fetchInsuranceDetails("MBSG", "S6564318I", "1234", "007");
assertNotNull("response is not null", bdsFetchInsuranceDetailsResponse);
}
public static ResponseEntity<BDSFetchInsuranceDetailsResponse> sampleBDSCustomerInsuranceHoldings() {
BDSFetchInsuranceDetailsResponse bdsResponse = new BDSFetchInsuranceDetailsResponse();
Header header = new Header();
header.setChannelId("MBSG");
header.setMsgId("4aBE50ZrQtjVuXfTyALJ");
bdsResponse.setHeader(header);
ResponseEntity<BDSFetchInsuranceDetailsResponse> response = new ResponseEntity<BDSFetchInsuranceDetailsResponse>(bdsResponse, HttpStatus.ACCEPTED);
return response;
}
}
My Actual class
#Component
public class BDSAdapter {
#Autowired
RestTemplate restTemplate;
#Autowired
BDSFetchInsuranceDetailsRequest bdsFetchInsuranceDetailsRequest;
#Autowired
Environment env;
public BDSFetchInsuranceDetailsResponse fetchInsuranceDetails(String channelId, String customerId,
String insurerCode, String policyNumber) throws InsuranceHoldingsException {
BDSFetchInsuranceDetailsResponse bdsFetchInsuranceDetailsResponse = null;
try {
logger.info("Inside BDSAdapter::fetchInsuranceDetails");
Header header = new Header();
header.setMsgId(RandomStringUtils.randomAlphanumeric(20));
header.setChannelId(channelId);
bdsFetchInsuranceDetailsRequest.setHeader(header);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<BDSFetchInsuranceDetailsRequest> requestEntity = new HttpEntity<>(bdsFetchInsuranceDetailsRequest, requestHeaders);
ResponseEntity<BDSFetchInsuranceDetailsResponse> response = restTemplate.exchange(env.getProperty("bds_fetch_insurance_details_url"),HttpMethod.POST, requestEntity, BDSFetchInsuranceDetailsResponse.class);
if(response.getStatusCodeValue() == 204) {
throw new InsuranceHoldingsException(response.getStatusCode().toString(), "No Content");
}
bdsFetchInsuranceDetailsResponse = response.getBody();
} catch (Exception e) {
e.printStackTrace();
}
}
return bdsFetchInsuranceDetailsResponse;
}
}
Perhaps, because mock of BDSFetchInsuranceDetailsResponse will return not a class of BDSFetchInsuranceDetailsResponse but some mockclass
Arugments inside restTemplate.exchange() method should match. In this code env.getProperty("bds_fetch_insurance_details_url") returns null which does not match with String. So it gives null response.
Added the below statment before When(restTemplate.exchange(-,-,-,-).thenReturn(myResponse). It is working
`Mockito.when(env.getProperty("bds_fetch_insurance_details_url")).thenReturn("anyString")`;
I have an API which throws ResponseStatusException in case of error :
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "My error msg");
In the client side I have :
RestTemplate restTemplate = this.builder.errorHandler(new RestTemplateResponseErrorHandler()).build();
// Then I use restTemplate to call URLs with restTemplate.exchange(...)
And the ResponseErrorHandler :
#Component
public class RestTemplateResponseErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse httpResponse) throws IOException {
return (httpResponse.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR || httpResponse.getStatusCode()
.series() == HttpStatus.Series.SERVER_ERROR);
}
#Override
public void handleError(ClientHttpResponse httpResponse) throws IOException {
String error = new String(httpResponse.getBody().readAllBytes(), StandardCharsets.UTF_8);
}
}
Problem is the getBody() is empty, and when I inspect the httpResponse I cannot find the "My error msg", it seems that it's not present in the object.
Is there any way to get the msg?
Thx
You need to build the RestTemplate instance using the RestTemplateBuilder like this:
public class xyz {
RestTemplate restTemplate;
#Autowired
public xyz(RestTemplateBuilder restTemplateBuilder) {
restTemplate = restTemplateBuilder
.errorHandler(new RestTemplateResponseErrorHandler())
.build();
}
}
Catch HttpClientErrorException as follows:
try{
HttpEntity<ProfileDto> entity = new HttpEntity<ProfileDto>(profileDto,headers);
response= restTemplate.postForEntity(environment.getProperty("app.filedownload.url"),entity, xxxxxx.class);
}catch(HttpClientErrorException | HttpServerErrorException e ){
if(HttpStatus.UNAUTHORIZED.equals(e.getStatusCode())){
//do your stufs
}
}catch(Exception e){
e.printStackTrace();
}
In the CarController class, I have a method for getting a Car instance by id. The question is whether to throw an exception or return a ResponseEntity<Car>(null, ...)
VERSION 1 Throwing exception if car's Id does not exist
#RestController
public class CarController {
#Autowired
private CarService service;
#GetMapping("cars/{id}")
public ResponseEntity<Car> getById(#PathVariable("id") long id) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
try {
Car car = service.getById(id);
return new ResponseEntity<Car>(car, headers, HttpStatus.OK);
}
catch(AppException ae) {
LOG.error("CarService could not get car with id {}", id);
throw ae;
}
}
}
VERSION 2 Returning null Car in ResponseEntity if id can't be found
#RestController
public class CarController {
#Autowired
private CarService service;
#GetMapping("cars/{id}")
public ResponseEntity<Car> getById(#PathVariable("id") long id) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
try {
Car car = service.getById(id);
return new ResponseEntity<Car>(car, headers, HttpStatus.OK);
} catch (AppException ae) {
LOG.error("CarService could not get car with id {}", id);
return new ResponseEntity<Car>(null, headers, HttpStatus.NOT_FOUND);
}
}
}
I would reccomend using the null element in combination with the HTTP/404 Status code.
If you simply throw an exception, the error handling is most likely to produce a 5XX-HTTP Status code, which means that there is an internal server error. However in your case, there should not be an internal server error, because the resource is simply not found.
See also: https://stackoverflow.com/a/2195675/6085896 (In this case the question is 2XX-Status vs 4XX-Status)
It should be
#RestController
public class CarController {
#Autowired
private CarService service;
#GetMapping("cars/{id}")
public ResponseEntity<Car> getById(#PathVariable("id") long id) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Car car = service.getById(id);
if (car == null) {
return new ResponseEntity<Car>(car, headers, HttpStatus.OK);
}
LOG.info("Car has id {} is not exist.", id);
return new ResponseEntity<Car>(null, headers, HttpStatus.NOT_FOUND);
}
}
because Return no object, it is not the same meaning with an Exception, return HttpStatus.NOT_FOUND is good enough.
I'm wondering how do I mock the rest controller for the code below,
public void sendData(ID id, String xmlString, Records record) throws ValidationException{
ClientHttpRequestFactory requestFactory = new
HttpComponentsClientHttpRequestFactory(HttpClients.createDefault());
RestTemplate restTemplate = new RestTemplate(requestFactory);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
restTemplate.setMessageConverters(messageConverters);
MultiValueMap<String,String> header = new LinkedMultiValueMap<>();
header.add("x-api-key",api_key);
header.add("Content-Type",content_type);
header.add("Cache-Control",cache_control);
HttpEntity<String> request = new HttpEntity<>(xmlString, header);
try {
restTemplate.postForEntity(getUri(id,record), request, String.class);
}catch (RestClientResponseException e){
throw new ValidationException("Error occurred while sending a file to some server "+e.getResponseBodyAsString());
}
}
Any suggestion would be helpful.
I tried to do something like this,
#RunWith(MockitoJUnitRunner.class)
public class Safe2RestControllerTest {
private MockRestServiceServer server;
private RestTemplate restTemplate;
private restControllerClass serviceToTest;
#Before
public void init(){
//some code for initialization of the parameters used in controller class
this.server = MockRestServiceServer.bindTo(this.restTemplate).ignoreExpectOrder(true).build();
}
#Test
public void testSendDataToSafe2() throws ValidationException, URISyntaxException {
//some code here when().then()
String responseBody = "{\n" +
" \"responseMessage\": \"Validation succeeded, message
accepted.\",\n" +
" \"responseCode\": \"SUCCESS\",\n" +
" 2\"responseID\": \"627ccf4dcc1a413588e5e2bae7f47e9c::0d86869e-663a-41f0-9f4c-4c7e0b278905\"\n" +
"}";
this.server.expect(MockRestRequestMatchers.requestTo(uri))
.andRespond(MockRestResponseCreators.withSuccess(responseBody,
MediaType.APPLICATION_JSON));
serviceToTest.sendDataToSafe2(id, xmlString, record);
this.server.verify();
}
}
This is the test case what I'm trying to do but it still calling actual rest api
As pointed out by #JBNizet, you should take a look at MockRestServiceServer. It allows you to test Spring components which are using RestTemplate to make HTTP calls.
See MockRestServiceServer and #RestClientTest.
I have a rest service which send an 404 error when the resources is not found.
Here the source of my controller and the exception which send Http 404.
#Controller
#RequestMapping("/site")
public class SiteController
{
#Autowired
private IStoreManager storeManager;
#RequestMapping(value = "/stores/{pkStore}", method = RequestMethod.GET, produces = "application/json")
#ResponseBody
public StoreDto getStoreByPk(#PathVariable long pkStore) {
Store s = storeManager.getStore(pkStore);
if (null == s) {
throw new ResourceNotFoundException("no store with pkStore : " + pkStore);
}
return StoreDto.entityToDto(s);
}
}
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException
{
private static final long serialVersionUID = -6252766749487342137L;
public ResourceNotFoundException(String message) {
super(message);
}
}
When i try to call it with RestTemplate with this code :
ResponseEntity<StoreDto> r = restTemplate.getForEntity(url, StoreDto.class, m);
System.out.println(r.getStatusCode());
System.out.println(r.getBody());
I receive this exception :
org.springframework.web.client.RestTemplate handleResponseError
ATTENTION: GET request for "http://........./stores/99" resulted in 404 (Introuvable); invoking error handler
org.springframework.web.client.HttpClientErrorException: 404 Introuvable
I was thinking I can explore my responseEntity Object and do some things with the statusCode. But exception is launch and my app go down.
Is there a specific configuration for restTemplate to not send exception but populate my ResponseEntity.
As far as I'm aware, you can't get an actual ResponseEntity, but the status code and body (if any) can be obtained from the exception:
try {
ResponseEntity<StoreDto> r = restTemplate.getForEntity(url, StoreDto.class, m);
}
catch (final HttpClientErrorException e) {
System.out.println(e.getStatusCode());
System.out.println(e.getResponseBodyAsString());
}
RESTTemplate is quite deficient in this area IMO. There's a good blog post here about how you could possibly extract the response body when you've received an error:
http://springinpractice.com/2013/10/07/handling-json-error-object-responses-with-springs-resttemplate
As of today there is an outstanding JIRA request that the template provides the possibility to extract the response body:
https://jira.spring.io/browse/SPR-10961
The trouble with Squatting Bear's answer is that you would have to interrogate the status code inside the catch block eg if you're only wanting to deal with 404's
Here's how I got around this on my last project. There may be better ways, and my solution doesn't extract the ResponseBody at all.
public class ClientErrorHandler implements ResponseErrorHandler
{
#Override
public void handleError(ClientHttpResponse response) throws IOException
{
if (response.getStatusCode() == HttpStatus.NOT_FOUND)
{
throw new ResourceNotFoundException();
}
// handle other possibilities, then use the catch all...
throw new UnexpectedHttpException(response.getStatusCode());
}
#Override
public boolean hasError(ClientHttpResponse response) throws IOException
{
return response.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR
|| response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR;
}
The ResourceNotFoundException and UnexpectedHttpException are my own unchecked exceptions.
The when creating the rest template:
RestTemplate template = new RestTemplate();
template.setErrorHandler(new ClientErrorHandler());
Now we get the slightly neater construct when making a request:
try
{
HttpEntity response = template.exchange("http://localhost:8080/mywebapp/customer/100029",
HttpMethod.GET, requestEntity, String.class);
System.out.println(response.getBody());
}
catch (ResourceNotFoundException e)
{
System.out.println("Customer not found");
}
Since it's 2018 and I hope that when people say "Spring" they actually mean "Spring Boot" at least, I wanted to expand the given answers with a less dust-covered approach.
Everything mentioned in the previous answers is correct - you need to use a custom ResponseErrorHandler.
Now, in Spring Boot world the way to configure it is a bit simpler than before.
There is a convenient class called RestTemplateBuilder. If you read the very first line of its java doc it says:
Builder that can be used to configure and create a RestTemplate.
Provides convenience methods to register converters, error handlers
and UriTemplateHandlers.
It actually has a method just for that:
new RestTemplateBuilder().errorHandler(new DefaultResponseErrorHandler()).build();
On top of that, Spring guys realized the drawbacks of a conventional RestTemplate long time ago, and how it can be especially painful in tests. They created a convenient class, TestRestTemplate, which serves as a wrapper around RestTemplate and set its errorHandler to an empty implementation:
private static class NoOpResponseErrorHandler extends
DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
}
}
You can create your own RestTemplate wrapper which does not throw exceptions, but returns a response with the received status code. (You could also return the body, but that would stop being type-safe, so in the code below the body remains simply null.)
/**
* A Rest Template that doesn't throw exceptions if a method returns something other than 2xx
*/
public class GracefulRestTemplate extends RestTemplate {
private final RestTemplate restTemplate;
public GracefulRestTemplate(RestTemplate restTemplate) {
super(restTemplate.getMessageConverters());
this.restTemplate = restTemplate;
}
#Override
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException {
return withExceptionHandling(() -> restTemplate.getForEntity(url, responseType));
}
#Override
public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException {
return withExceptionHandling(() -> restTemplate.postForEntity(url, request, responseType));
}
private <T> ResponseEntity<T> withExceptionHandling(Supplier<ResponseEntity<T>> action) {
try {
return action.get();
} catch (HttpClientErrorException ex) {
return new ResponseEntity<>(ex.getStatusCode());
}
}
}
Recently had a usecase for this. My solution:
public class MyErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
return hasError(clientHttpResponse.getStatusCode());
}
#Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
HttpStatus statusCode = clientHttpResponse.getStatusCode();
MediaType contentType = clientHttpResponse
.getHeaders()
.getContentType();
Charset charset = contentType != null ? contentType.getCharset() : null;
byte[] body = FileCopyUtils.copyToByteArray(clientHttpResponse.getBody());
switch (statusCode.series()) {
case CLIENT_ERROR:
throw new HttpClientErrorException(statusCode, clientHttpResponse.getStatusText(), body, charset);
case SERVER_ERROR:
throw new HttpServerErrorException(statusCode, clientHttpResponse.getStatusText(), body, charset);
default:
throw new RestClientException("Unknown status code [" + statusCode + "]");
}
}
private boolean hasError(HttpStatus statusCode) {
return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR ||
statusCode.series() == HttpStatus.Series.SERVER_ERROR);
}
There is no such class implementing ResponseErrorHandler in Spring framework, so I just declared a bean:
#Bean
public RestTemplate getRestTemplate() {
return new RestTemplateBuilder()
.errorHandler(new DefaultResponseErrorHandler() {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
//do nothing
}
})
.build();
}
The best way to make a RestTemplate to work with 4XX/5XX errors without throwing exceptions I found is to create your own service, which uses RestTemplate :
public ResponseEntity<?> makeCall(CallData callData) {
logger.debug("[makeCall][url] " + callData.getUrl());
logger.debug("[makeCall][httpMethod] " + callData.getHttpMethod());
logger.debug("[makeCall][httpEntity] " + callData.getHttpEntity());
logger.debug("[makeCall][class] " + callData.getClazz());
logger.debug("[makeCall][params] " + callData.getQueryParams());
ResponseEntity<?> result;
try {
result = restTemplate.exchange(callData.getUrl(), callData.getHttpMethod(), callData.getHttpEntity(),
callData.getClazz(), callData.getQueryParams());
} catch (RestClientResponseException e) {
result = new ResponseEntity<String>(e.getResponseBodyAsString(), e.getResponseHeaders(), e.getRawStatusCode());
}
return result;
}
And in case of exception, simply catch it and create your own ResponseEntity.
This will allow you to work with the ResponseEntity object as excepted.