I am developing a Spring Boot application/library where i have a Service that sends a request to a an API. When in integration Test the Entry Point into the Service is an /integration Endpoint. The following Code is the mvp to reproduce the error
Main Service:
@Service
@RequiredArgsConstructor
public class CalculationService {
private final RestClient restClient;
public CalculationResponse calculate(CalculationRequest request){
return restClient.post().uri("/service/calculate")
.body(request)
.retrieve()
.toEntity(CalculationResponse.class).getBody();
}
}
Main Controller
@RestController
@RequestMapping("/service")
public class MockApi {
@PostMapping("/calculate")
public CalculationResponse calculate(@RequestBody CalculationRequest calculationRequest) {
CalculationResponse calculationResponse = new CalculationResponse();
calculationResponse.setSummary(calculationRequest.getName()+ "----" + calculationRequest.getValue());
return calculationResponse;
}
}
The Entry Point for my Integration Test
@RestController
@RequestMapping
@RequiredArgsConstructor
public class TestController {
private final CalculationService calculationService;
@PostMapping("/integration")
public CalculationResponse integration(@RequestBody CalculationRequest request) {
return calculationService.calculate(request);
}
}
RestClient Configuration
@Configuration
public class CalculationConfiguration {
@Bean
public RestClient restClient() {
return RestClient.builder()
.baseUrl("http://localhost:8080")
.build();
}
}
Integration Test
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith(SpringExtension.class)
public class IntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
@Test
public void thatSummaryIsReturned() throws Exception {
CalculationRequest request = new CalculationRequest("Test", "Value");
mockMvc.perform(MockMvcRequestBuilders.post("/integration")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk())
.andReturn();
}
}
When running the test i receive following error:
Request processing failed: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8080/service/calculate": null jakarta.servlet.ServletException: Request processing failed: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8080/service/calculate": null at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1022) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72) at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
When i change the endpoint in my integration test directly to /service/calculate it works. I tried to debug but when having /integration as path in the test i dont even react the other endpoint. Has anyone an idea why this is the case ?
Edit: When the Application runs in the Background it works. So it seems that there is something missing in my TestContext ...
The issue occurs because you are using @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
, which starts your application on a random port. However, your RestClient
is explicitly configured to use http://localhost:8080
, leading to a port mismatch. Since the application is not actually running on port 8080 during the test, requests fail with an I/O error when trying to reach http://localhost:8080/service/calculate
.
One solution is to start the test on a defined port:
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
properties = "server.port=8080"
)