Tenho a seguinte classe de controlador no meu aplicativo Spring Boot.
package com.tsdevelopment.springbootrest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
import java.util.List;
@RestController
public class SpringController {
private List<String> myList = Arrays.asList("Item 1", "Item 2");
@Autowired
RestTemplate restTemplate;
@GetMapping("/v1/resources")
public ResponseEntity<List<String>> getAllResources(@RequestParam(required = false) String parameter){
return ResponseEntity.ok(myList);
}
//API to API call
@GetMapping("/v1/resources/inter-api-call")
public ResponseEntity<List<String>> apiCall(){
List<String> apiResponse = restTemplate.getForEntity("http://localhost:8083/v1/resources", List.class).getBody();
return ResponseEntity.ok(apiResponse);
}
}
E estou tentando testar isso com a seguinte classe:
package com.tsdevelopment.springbootrest;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import java.util.Arrays;
import java.util.List;
import org.hamcrest.Matchers;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringControllerTest {
@InjectMocks
SpringController springController;
@Mock
RestTemplate restTemplate;
@LocalServerPort
private int port;
@BeforeEach
public void setUp(){
RestAssured.port = port;
MockitoAnnotations.openMocks(this);
}
@Test
public void apiCall(){
Mockito.when(restTemplate.getForEntity("http://localhost:8083/v1/resources", List.class)).thenReturn(ResponseEntity.ok(Arrays.asList("Item 1", "Item 2")));
System.out.println("RestTemplate response: " + restTemplate.getForEntity("http://localhost:8083/v1/resources", List.class));
System.out.println("Controller response: " + springController.apiCall());
RestAssured.given()
.when()
.get("/v1/resources/inter-api-call")
.then()
.statusCode(200)
.body("", Matchers.equalTo("[\"Item 1\",\"Item 2\"]"));
}
}
Quando executo o teste ele falha. Os logs que recebo são:
Resposta do RestTemplate: <200 OK OK,[Item 1, Item 2],[]> Resposta do controlador: <200 OK OK,[Item 1, Item 2],[]>
E o log de erros do teste é:
java.lang.AssertionError: 1 expectativa falhou. Código de status esperado <200>, mas era <500>.
Em vez de usar @Mock no RestTemplate, você deve usar @MockBean, o que garante que o Spring Boot use seu mock no contexto.
Ao usar @Mock, você está criando uma instância simulada do objeto que só existe dentro da própria classe de teste. Essa simulação não é visível para o mecanismo de injeção de dependência do Spring, então o spring ainda está usando o bean RestTemplate real do seu contexto durante a execução real do teste.
Remova também a anotação @InjectMocks porque ela é desnecessária, já que você está contando com a injeção de dependência do Spring com @Autowired.