I have a resttemplatebuilder in my resttemplate service as
restTemplate = restTemplateBuilder
.errorHandler(myResponseErrorHandler)
.build();
ResponseEntity<String> result = restTemplate.exchange(builder.buildAndExpand(urlParams).toUri(),
HttpMethod.GET, requestEntity, String.class);
i have written a junit using mockito, i am mocking the resttemplate like below:
Mockito.when(restTemplate.exchange(builder.buildAndExpand(urlParams).toUri(), HttpMethod.GET, requestEntity, String.class)).thenReturn(new ResponseEntity<String>(details.toString(),HttpStatus.OK));
when i run my junit, i am keep on getting null pointer exception at below step in my source code service
restTemplate = restTemplateBuilder
.errorHandler(myResponseErrorHandler)
can anyone help me to understand the problem?
Related
I have written following Junit for the method which is using rest template to call another service but the mocking is throwing URI not absolute error.
Method:
#Value("${app.prop.esso-url}")
private String essoUrl;
public ResponseEntity<String> essoCall(String token) {
ResponseEntity<String> response=null;
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);
headers.setBearerAuth(token);
HttpEntity<String> entity = new HttpEntity<>(headers);
response= restTemplate.exchange(essoUrl, HttpMethod.GET, entity,String.class);
logger.info("successfully received response.");
return response;
}
Junit:
#Autowired
private ObjectMapper objectMapper;
#InjectMocks
SecurityMsServiceImpl securityservice=new SecurityMsServiceImpl();
#Value("${app.prop.esso-url}")
private String essoUrl;
#Mock
private RestTemplate restTemplate;
#Test
void givenMockingIsDoneByMockito_whenGetIsCalled_shouldReturnMockedObject() {
ResponseEntity<String> responseEntity = new ResponseEntity<String>("success", HttpStatus.OK);
Mockito
.when(restTemplate.getForEntity(essoUrl
, String.class))
.thenReturn(responseEntity);
ResponseEntity<String> finalresponse=securityservice.essoCall("abc");
assertEquals(responseEntity, finalresponse);
}
I see this problem time and time again:
You are constructing new instance of RestTemplate in your method under test.
What you should do instead is to inject it into your bean, which means:
add a field of type RestTemplate in your bean
initialize it in constructor
in tests, pass in mocked instance
in prod, pass in a regular instance via Spring DI
RestTemplate is thread-safe and one instance of it is perfectly valid.
See Is RestTemplate thread safe?
On top of that:
my advice is to use constructor injection instead of field injection
you are mixing Spring's #Autowired with Mockito's #InjectMocks. Choose if you want to test with MockitoExtension or SpringExtension and pick the appropriate set of annotations to initialize your SUT.
I have a springboot rest Service A calling rest service B using restTemplate.
Rest service A's restTemplate bean is created as follows with timeout settings as seen in the code snippet below.
#Bean
RestTemplate getRestTemplate()
{
CloseableHttpClient closeableHttpClient = HttpClientBuilder.create().build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(closeableHttpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
requestFactory.setConnectTimeout( 2000 );
requestFactory.setReadTimeout( 2000 );
return restTemplate;
}
A calls B as follows:
try{
restTemplate.postForEntity(urlSvcB, httpEntity, myObject.class);
}
catch (Exception ex){
.....some code here.....
}
When I put both A and B in bebug mode and wait at a breakpoint in B for more than 2 seconds, I except restTemplate call in A to detect a timeout of 2 seconds and straight away go in to the exception block BUT it doesn't. I also put a thread.sleep(5000) in B but still in vain.
Is there anything wrong I am doing because of which I don't see the expected ?
If you are using spring boot, then you could try:
#Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder)
{
return restTemplateBuilder
.setConnectTimeout(...)
.setReadTimeout(...)
.build();
}
If that is not okay, then in your current code, try setting all the props on requestFactory before creating a restTemplate OR test once by getting rid of CloseableHTTPClient like:
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setConnectionRequestTimeout(...);
httpRequestFactory.setConnectTimeout(...);
httpRequestFactory.setReadTimeout(...);
return new RestTemplate(httpRequestFactory);
In Spring Boot I'm trying to create a RestTemplate which will use basic authentication using
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
builder.basicAuthorization("username", "password");
RestTemplate template = builder.build();
return template;
}
I then inject the RestTemplate in my service class as
#Autowired
private RestTemplate restTemplate;
However, my requests fail with a 401 unauthorized exception:
Caused by: org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
Using another REST Client (Postman) the requests to the same URL succeeds so I assume the basic authentication is not working correctly. From the debug output it looks as if the authentication header is not being set. What will make this work?
The problem is that you are using the RestTemplateBuilder in a wrong way. The RestTemplateBuilder is immutable. So when doing builder.basicAuthorization("username", "password") you actually get a new instance, with a BasicAuthorizationInterceptor added and configured, of the RestTemplateBuilder. (this applies to all configuration methods of the RestTemplateBuilder they all create a fresh copied instance).
However your code is discarding that specifically configured instance and you are basically using the non secured default RestTemplateBuilder.
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
builder.basicAuthorization("username", "password");
RestTemplate template = builder.build();
return template;
}
This code should be replaced with something like this.
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.basicAuthorization("username", "password").build();
}
Which will use the specifically configured instance.
One solution is to create the RestTemplate as follows:
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate template = builder.build();
template.setMessageConverters(
Arrays.asList(
new FormHttpMessageConverter(),
new StringHttpMessageConverter()
)
);
template.getInterceptors().add(new BasicAuthorizationInterceptor("username", "password"));
return template;
}
I created this restTemplate bean..
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.messageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()))
.build();
}
as you can see i set the messageConverter.
but when i used the bean inside my service class ... i get an error like this
"RestClientException: Could not write request: no suitable HttpMessageConverter found for request type . . "
here is the code in my service class . .
ResponseEntity<ProcessGroupEntity> response = this.restTemplate.exchange(RequestEntity.post(new URI(uri))
.contentType(MediaType.APPLICATION_JSON)
.body(foo)
, Foo.class);
now i tried setting message converter before calling exchange.
this.restTemplate.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
ResponseEntity<ProcessGroupEntity> response = this.restTemplate.exchange(RequestEntity.post(new URI(uri))
.contentType(MediaType.APPLICATION_JSON)
.body(foo)
, Foo.class);
and the code works. so my question is why RestTemplateBuilder's messageConverter() method seems to fail in setting/adding the converter.?
edit:
here is how i inject the restTemplate bean in my service class
private final RestTemplate restTemplate;
...
#Autowired
public SomeService(RestTemplate restTemplate,
...) {
this.restTemplate = restTemplate;
}
Is it possible to write AOP for Spring RestTemplate class using spring AOP or Aspectj. EX:
#Around("execution(* org.springframework.web.client.RestTemplate.getFor*(..))")
Thanks
I had the same issue and couldn't make it work with AOP.
However, in this case, there is a workaround. Since RestTemplate extends InterceptingHttpAccessor, you can intercept all requests coming through theRestTemplate object.
Sample configuration that logs all HTTP requests :
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
// (...)
// setup code for the RestTemplate object
restTemplate.getInterceptors().add((request, body, execution) -> {
logger.info("HTTP {} request to {}", request.getMethod(), request.getURI());
return execution.execute(request, body);
});
return restTemplate;
}
While this is not equivalent to using an aspect, you can get similar functionality with interceptors and pretty minimal configuration.