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
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
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
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);
}
}
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);
}
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>?