RestClientTest annotation fails to autocnfigure RestTemplate - java

I have a class with RestTemplate dependency
#Service
public class SwiftAPIPushHandler {
private final ObjectMapper objectMapper;
private final RestTemplate restTemplate;
#Value("${app.swift.host}")
private String swiftHost;
#Value("${app.swift.client.secret}")
private String clientSecret;
public SwiftAPIPushHandler(#Autowired RestTemplate restTemplate,
#Autowired ObjectMapper objectMapper) {
this.restTemplate = restTemplate;
this.objectMapper = objectMapper;
}
#ServiceActivator
public Map<String, Object> outboundSwiftPushHandler(Map<String, Object> payload,
#Header("X-UPSTREAM-WEBHOOK-SOURCE") String projectId) throws JsonProcessingException {
// HTTP POST Request from RestTemplate here
}
}
And in the test I wish to use #RestClientTest for auto configuring the RestTemplate
#RestClientTest
#SpringJUnitConfig(classes = {SwiftAPIPushHandler.class})
public class SwiftAPIPushHandlerTest {
#Autowired
SwiftAPIPushHandler apiPushHandler;
#Test
public void testSwiftApiPush(
#Value("classpath:sk-payloads/success-response.json") Resource skPayload) throws IOException {
// blah blah blah
}
}
But the test fails with unable to find autowiring candidate for RestTemplate error.
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.swift.cloud.transformation.engine.SwiftAPIPushHandler required a bean of type 'org.springframework.web.client.RestTemplate' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'org.springframework.web.client.RestTemplate' in your configuration.

In the documentation of #RestClientTest you can read:
If you are testing a bean that doesn't use RestTemplateBuilder but instead injects a RestTemplate directly, you can add #AutoConfigureWebClient(registerRestTemplate = true).
So if you add to your test class #AutoConfigureWebClient(registerRestTemplate = true) it should properly inject the restTemplate.
#RestClientTest
#AutoConfigureWebClient(registerRestTemplate = true)
#SpringJUnitConfig(classes = {SwiftAPIPushHandler.class})
public class SwiftAPIPushHandlerTest {
The alternative is injecting a RestTemplateBuilder in your service, in this case you don't need the #AutoConfigureWebClient annotation in your test:
#Autowired
public SwiftAPIPushHandler(RestTemplateBuilder restTemplateBuilder, ObjectMapper objectMapper) {
this.restTemplate = restTemplateBuilder.build();
this.objectMapper = objectMapper;
}

you need to configure resttemplate with resttemplate builder
add this to your project
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
Duration duration = Duration.ofMinutes(1);
return builder.setConnectTimeout(duration).setReadTimeout(duration).build();
}
}

Related

The dependencies of some of the beans in the application context form a cycle #RequiredArgsConstructor

In my Config class I have bean with RestTemplate and I had to add CurrencyConverterService currencyConverter becoues I am using it in modelMapper.
Config class:
#Configuration
#RequiredArgsConstructor
public class Config {
private final CurrencyConverterService currencyConverter;
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
... }
but in currencyConverter I have to use RestTemplate to get current currency rates from api so I added RestTemplate:
#Service
#RequiredArgsConstructor
public class CurrencyConverterService {
private static String BASE_URL = url...;
private final RestTemplate restTemplate;
private final CurrencyEntityRepository currencyEntityRepository;
and of course I am getting error:
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| config defined in file [C:\config\Config.class]
↑ ↓
| currencyConverterService defined in file [C\service\CurrencyConverterService.class]
└─────┘
I was trying to add #Lazy up to restTemplate in CurrencyService but it does not help.
spring.main.allow-circular-references= true did not help too
What about
#Configuration
public class Config {
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Bean
public ModelMapper modelMapper(final CurrencyConverterService currencyConverter) {
ModelMapper modelMapper = new ModelMapper();
...
}
...
}
when you create a bean you can pass other beans as parameter
Solution 1: use javax.inject.Provider
Assuming you are using Maven you can add the following dependency:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
And then in CurrencyConverterService inject an instance of javax.inject.Provider of RestTemplate:
import javax.inject.Provider;
...
#Service
#RequiredArgsConstructor
public class CurrencyConverterService {
...
private final Provider<RestTemplate> restTemplate;
And retrieve the instance with restTemplate.get().
However this will still fail during runtime in case of circular usage.
Solution 2: resolve cyclic dependency
Assuming you do not need the CurrencyConverterService for constructing RestTemplate the more stable solution is to create a second class producing the RestTemaplate (you can have as many classes annotated with #Configuration or #Component as you want):
#Configuration
public class RestTemplateProducer {
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
You can write #Lazy at the top of the #Autowired in of the class where you are injecting dependencies.
As that will break the cycle.
However you have to write it on the top of #Autowired

Cannot Autowire Qualifier tagged RestTemplate from unit test

I'm using an remote api in my web application. Some endpoints require an authentication header in the API I'm using. Because of this need I have created two different RestTemplateBuilder bean in my configuration class.
#Configuration
public class RestTemplateConfiguration {
#Bean
#DependsOn(value = {"secureRestTemplateCustomizer"})
#Qualifier("secureRestTemplateBuilder")
public RestTemplateBuilder secureRestTemplateBuilder() {
return new RestTemplateBuilder(secureRestTemplateCustomizer());
}
#Bean
#DependsOn(value = {"publicRestTemplateCustomizer"})
#Qualifier("publicRestTemplateBuilder")
public RestTemplateBuilder publicRestTemplateBuilder() {
return new RestTemplateBuilder(publicRestTemplateCustomizer());
}
#Bean
#Qualifier("secureRestTemplateCustomizer")
public SecureRestTemplateCustomizer secureRestTemplateCustomizer() {
return new SecureRestTemplateCustomizer();
}
#Bean
#Qualifier("publicRestTemplateCustomizer")
public PublicRestTemplateCustomizer publicRestTemplateCustomizer() {
return new PublicRestTemplateCustomizer();
}
}
And these are my custom RestTemplateCustomizers
#Component
public class SecureRestTemplateCustomizer implements RestTemplateCustomizer {
#Override
public void customize(RestTemplate restTemplate) {
restTemplate.setErrorHandler(new ErrorHandler());
restTemplate.getInterceptors().add(new AuthorizationHeaderInterceptor());
}
}
PublicRestTemplateCustomizer
#Component
public class PublicRestTemplateCustomizer implements RestTemplateCustomizer {
#Override
public void customize(RestTemplate restTemplate) {
restTemplate.setErrorHandler(new ErrorHandler());
}
}
There is no problem when I want to use these RestTemplateBuilders in my api clients like below. Spring can autowire them into my api client constructor.
private RestTemplate restTemplate;
#Autowired
public LoginApiClient(#Qualifier("publicRestTemplateBuilder") RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
But in my unit tests this usage is firing an error like
Error creating bean with name 'loginApiClient' defined in file [..\LoginApiClient.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.web.client.RestTemplateBuilder' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Qualifier(value=publicRestTemplateBuilder)}
#RunWith(SpringRunner.class)
#RestClientTest({LoginApiClient.class})
#Category(IUnitTest.class)
public class LoginApiClientTest {
#Autowired ILoginApiClient loginApiClient;
#Autowired private MockRestServiceServer server;
#Test
public void validateToken_returns_true_for_valid_token() throws Exception{
String token = "token";
this.server.expect(requestTo(this.validateTokenUrl))
.andExpect(method(HttpMethod.POST))
.andRespond(withSuccess(objectMapper.writeValueAsString(validTokenResponse(token)), MediaType
.APPLICATION_JSON));
Boolean isValid = loginApiClient.validateToken(token);
server.verify();
assertThat(isValid,is(equalTo(true)));
}
}
How can I mock these RestTemplates truly and use in my unit test.
I think you are missing the #ComponentScan(value = "your.package") in your Main application class
You do not have context specified.
Try adding this annotation to uour test class:
#ContextConfiguration(classes=RestTemplateConfiguration.class, loader=AnnotationConfigContextLoader.class)
See this article for details: spring-3-1-m2-testing-with-configuration-classes-and-profiles - it's for Spring 3, but newer versions of Spring work similarly.
Here's a newer tutorial: junit-spring-integration-example

Spock Spring inject mock and bean via constructor

I'm looking for way how to inject beans and mock in single constructor.
#Service
public class SomeService {
private EndpointUrlProvider endpointUrlProvider;
private RestTemplate restTemplate;
#Autowired
public SomeService(EndpointUrlProvider endpointUrlProvider, RestTemplate restTemplate){
this.endpointUrlProvider = endpointUrlProvider;
this.restTemplate = restTemplate;
}
Test:
class SomeTest extends Specification {
#Autowired
EndpointUrlProvider endpointUrlProvider
RestTemplate restTemplate = Mock {
postForEntity(_, _, SomeResponse.class) >> new ResponseEntity(new SomeResponse(), HttpStatus.OK)
}
SomeService someService = new SomeService(endpointUrlProvider, restTemplate)
//some tests
}
When I fire test my endpointUrlProvider in someService is null. What I did wrong ? What is the best way to test this?
As far as I see, you are trying to do partial mocking. To inject Spring beans, first you will need TestContextManager. Therefore, run the test with SpringRunner or SpringJUnit4ClassRunner. This should do the work:
#RunWith(SpringRunner.class)
public class SomeServiceTest {
#Autowired
private EndpointUrlProvider endpointUrlProvider;
#Before
public setUp() {
RestTemplate restTemplate = mock(RestTemplate.class);
SomeService someService = new SomeService(endpointUrlProvider, restTemplate);
}
}

Spring-Boot RestClientTest not correctly auto-configuring MockRestServiceServer due to unbound RestTemplate

EDIT: This question is specifically pertaining to the #RestClientTest annotation introduced in spring-boot 1.4.0 which is intended to replace the factory method.
Problem:
According to the documentation the #RestClientTest should correctly configure a MockRestServiceServer to use when testing a REST client. However when running a test I am getting an IllegalStateException saying the MockServerRestTemplateCustomizer has not been bound to a RestTemplate.
Its worth noting that I'm using Gson for deserialization and not Jackson, hence the exclude.
Does anyone know how to correctly use this new annotation? I haven't found any examples that require more configuration then when I have already.
Configuration:
#SpringBootConfiguration
#ComponentScan
#EnableAutoConfiguration(exclude = {JacksonAutoConfiguration.class})
public class ClientConfiguration {
...
#Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder()
.rootUri(rootUri)
.basicAuthorization(username, password);
}
}
Client:
#Service
public class ComponentsClientImpl implements ComponentsClient {
private RestTemplate restTemplate;
#Autowired
public ComponentsClientImpl(RestTemplateBuilder builder) {
this.restTemplate = builder.build();
}
public ResponseDTO getComponentDetails(RequestDTO requestDTO) {
HttpEntity<RequestDTO> entity = new HttpEntity<>(requestDTO);
ResponseEntity<ResponseDTO> response =
restTemplate.postForEntity("/api", entity, ResponseDTO.class);
return response.getBody();
}
}
Test
#RunWith(SpringRunner.class)
#RestClientTest(ComponentsClientImpl.class)
public class ComponentsClientTest {
#Autowired
private ComponentsClient client;
#Autowired
private MockRestServiceServer server;
#Test
public void getComponentDetailsWhenResultIsSuccessShouldReturnComponentDetails() throws Exception {
server.expect(requestTo("/api"))
.andRespond(withSuccess(getResponseJson(), APPLICATION_JSON));
ResponseDTO response = client.getComponentDetails(requestDto);
ResponseDTO expected = responseFromJson(getResponseJson());
assertThat(response, is(expectedResponse));
}
}
And the Exception:
java.lang.IllegalStateException: Unable to use auto-configured MockRestServiceServer since MockServerRestTemplateCustomizer has not been bound to a RestTemplate
Answer:
As per the answer below there is no need to declare a RestTemplateBuilder bean into the context as it is already provided by the spring-boot auto-configuration.
If the project is a spring-boot application (it has #SpringBootApplication annotation) this will work as intended. In the above case however the project was a client-library and thus had no main application.
In order to ensure the RestTemplateBuilder was injected correctly in the main application context (the bean having been removed) the component scan needs a CUSTOM filter (the one used by #SpringBootApplication)
#ComponentScan(excludeFilters = {
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class)
})
The MockRestServiceServer instance should be constructed from the static factory, using a RestTemplate. See this article for a detailed description of the testing process.
In your example, you can do:
#RunWith(SpringRunner.class)
#RestClientTest(ComponentsClientImpl.class)
public class ComponentsClientTest {
#Autowired
private ComponentsClient client;
#Autowired
private RestTemplate template;
private MockRestServiceServer server;
#Before
public void setUp() {
server= MockRestServiceServer.createServer(restTemplate);
}
/*Do your test*/
}
You have RestTemplateBuilder at two places. At ClientConfiguration class and at ComponentsClientImpl class. Spring boot 1.4.0 auto-configure a RestTemplateBuilder which can be used to create RestTemplate instances when needed. Remove below code from ClientConfiguration class and run your test.
#Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder()
.rootUri(rootUri)
.basicAuthorization(username, password);
}

Constructing RestTemplates with Spring #Configuration

Say I have some application-wide configuration:
#Configuration
public class RestTemplateConfiguration {
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public HttpClient anHttpClient() {
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
// set some properties
// ...
return HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager).build();
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ClientHttpRequestInterceptor aRequestInterceptor() {
....
}
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public ClientHttpRequestInterceptor anotherRequestInterceptor() {
....
}
}
Then I have some particular service that looks like:
#Service
public class MyService {
private final RestTemplate myParticularRestTemplate;
#Autowired
public MyService(RestTemplate myParticularRestTemplate) {
this.myParticularRestTemplate = myParticularRestTemplate;
}
/***
* Some incredible application logic
***/
#Configuration
public static class Config {
private int SOME_READ_TIMEOUT;
private int SOME_CONNECT_TIMEOUT;
#Bean
public RestTemplate myParticularRestTemplate(HttpClient anHttpClient, List<ClientHttpRequestInterceptor> interceptors) {
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(anHttpClient);
clientHttpRequestFactory.setReadTimeout(SOME_READ_TIMEOUT);
clientHttpRequestFactory.setConnectTimeout(SOME_CONNECT_TIMEOUT);
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
}
}
I want the interceptors to be injected into RestTemplates system-wide, but, as it is, because I've got to access nested objects of the RestTemplate to set particular configuration, I need to instantiate the RestTemplate myself -- at least as I understand it.
Is there a nicer way to address this that doesn't require configuration for specific RestTemplates to know about the List<ClientHttpRequestInterceptor>?

Categories

Resources