Inject TestRestTemplate for RestTemplate - java

I have a component which is auto wiring RestTemplate. I want to use that component for testing, but want to use TestRestTemplate. As I can see, TestRestTRemplate and RestTemplate is not connected through any interface.
RestTemplate implements RestOperations but TestRestTemplate doesn't. Is there elegant way to inject TestRestTemplate in RestTemplate, or some other alternate to achieve it?
I have created my own version of RestTemplate to use it in test and prod
#Autowired
private MyRestTemplate restTemplate;
public class MyRestTemplate {
#Autowired
private RestTemplate restTemplate;
}
#Configuration
#Profile("test")
public class MyConfig {
#Bean
public RestTemplate restTemplate() {
return new TestRestTemplate().getRestTemplate();
}
}

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

WebMvcTest with real service implementation

how can I create a "quasi" MVC integration test in Spring Boot. I would like to use my real implementation of a service, but I can't mange to do it. How can I inject real implementation instead of a mock.
My classes look like this
#Controller
#RequiredArgsConstructor
public class DashboardController {
private final RolesManagerService rolesManagerService;
private final ServletRequestManagerService servletRequestManagerService;
#GetMapping({"/", "/dashboard"})
public String index(Model model, HttpServletRequest httpServletRequest) {
model.addAttribute("canAddNewOrder", rolesManagerService.canRoleAccessApplicationPart(servletRequestManagerService.getRole(httpServletRequest), ApplicationPart.CREATE_NEW_ORDER));
model.addAttribute("var", "test");
return "dashboard";
}
}
and the test
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = DashboardController.class)
#AutoConfigureMockMvc
class IndexControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private UserDetailsService userDetailsService;
#MockBean
RolesManagerService rolesManagerService;
#MockBean
private ServletRequestManagerService servletRequestManagerService;
#Test
void testDashboard() throws Exception {
mockMvc.perform(get("/dashboard").with(user("admin").password("pass").roles("USER","ADMIN")))
.andExpect(status().isOk())
.andExpect(view().name("dashboard"))
.andExpect(xpath("//a").nodeCount(1))
.andExpect(model().attributeExists("canAddNewOrder"))
.andExpect(model().size(2))
.andExpect(model().attribute("var", equalTo("test")))
.andExpect(model().attribute("canAddNewOrder", equalTo(false)))
.andDo(print());
}
}
Generally WebMvcTest will not create a full Spring context with all components (Service etc) injected, but only the Controller you define. Either use a full SpringBootTest, or add something like this in your WebMvcTest class:
#TestConfiguration
static class AdditionalTestConfig {
#Bean
public RolesManagerService getService() {
return new RolesManagerService();
}
}

RestClientTest annotation fails to autocnfigure RestTemplate

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

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

Categories

Resources