Mock con RestTemplate en Spring
Aunque cada vez es menor el uso de RestTemplate, ya que no esta mantenido, por clientes reactivos como WebClient o declarativos como Feign, RestTemplate todavía es muy usado y existe en muchas aplicaciones, por ese motivo vamos a ver como hacer Mock con RestTemplate en Spring.
Una parte muy importante de nuestras aplicaciones y que no debemos de olvidar son nuestros test y cuando en nuestra aplicación hacemos uso de RestTemplate se puede complicar un poco. Por ello vamos a ver como hacer uso de Mockito para poder Mockear un RestTemplate.
¿Qué es RestTemplate en Spring?
En muchas ocasiones en nuestras aplicaciones necesitamos realizar alguna conexión vía rest con un API de terceros o una llamada a otro microservicio/servicio. Para esas ocasiones vamos a hacer uso de un componente de Spring, RestTemplate.
RestTemplate es un cliente HTTP y síncrono para permitir conexiones y accesos por HTTP.
¿Qué es Mockito?
Mockito es una herramienta open source orientada al testing para Java. Este framework permite la creación de una capa y batería de test para probar nuestras funcionalidades, así como test automatizados con el propósito de test-driven development o behavior-driven development.
Uso de Mockito con RestTemplate en Spring
El módulo de test de Spring trae un mock server que nos va a facilitar las labores de testing con nuestro RestTemplate, es el MockRestServiceServer. Al igual que hacemos con Wiremock, vamos a configurar y a levantar nuestro servidor con un objeto que devuelva en función de una petición un objeto concreto.
Cuando en la ejecución de nuestro test integrado lleguemos a la parte en la que se tiene que ejecutar nuestro RestTemplate, Mockito intercepta la llamada y se devolverá el objeto que hemos definido.
Ejemplo con Mockito y RestTemplate en Spring
A continuación vamos a ver un ejemplo con MockRestServiceServer para simular una llamada con RestTemplate.
Para poder realizar este ejemplo vamos a crear dos microservicios con comunicación HTTP a través de un restTemplate.
Nuestro ejemplo va a consistir en un microservicio de usuarios y otro microservicio de cuentas bancarias. Cada microservicio tendrá su propia Base de Datos (H2).
Cada vez que se soliciten los datos de un usuario también se tendrán que devolver la información de su cuenta bancaria por lo que, a través de un restTemplate se solicitarán al microservicio de cuentas.
Para ir directo al código se puede obtener de aquí.
Dependencias Maven
Vamos a comenzar con las dependencias necesarias, para ello hacemos uso de spring initializr.
Las dependencias de nuestro pom (ambos igual) imprescindibles para nuestro ejemplo serían las siguientes:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
Estas dependencias nos van a incluir las librerías necesarias para comunicarnos con BBDD, hacer uso de mockito y tener un resttemplate para comunicación.
Crear Test Integrado con RestTemplate
A continuación vamos a crear un test de un ejemplo de comunicación entre los dos servicios que tenemos para nuestro ejemplo.
Para poder hacer un test integrado con un restTemplate vamos a hacer uso de la clase MockRestServiceServer, la cual nos permitirá simular nuestra llamada.
Para poder inyectar el contexto y realizar test integrados vamos a hacer uso de las siguientes anotaciones:
@SpringBootTest(classes = { UserMockResttemplateApplication.class}, webEnvironment = WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc
Arrancar MockRestServiceServer
Para ello en nuestra clase de test arrancaremos el servidor de la siguiente manera:
mockServer = MockRestServiceServer.createServer(restTemplate);
El anterior comando deberemos ejecutarlo al iniciar nuestros test, por lo que lo lógico sería acompañarlo con un @BeforeEach, por ejemplo.
Simular llamada con mock de RestTemplate
Una vez hemos arrancado nuestro servidor vamos a simular la llamada. Para simular la llamada haremos uso de nuestro objeto mockServer, al que le podemos indicar los siguientes parámetros:
- ExpectedCount: Nos va a permitir establecer número de veces a ejecutar el mock de nuestro restTemplate.
- RequestTo: Url a ejecutar.
- AndExpect : tipo de verbo que vamos a ejecutar con nuestra url.
- AndRespond: Status code que esperamos si añadimos withStatus o podemos añdir withSuccess JSON que esperamos.
mockServer.expect(ExpectedCount.once(), requestTo(new URI("http://localhost:8081/accounts/0060a6e2-f47e-42b5-8cb3-a45932920204"))) .andExpect(method(HttpMethod.GET)) .andRespond(withStatus(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON));
Estan son algunas de las posibilidades que existen.
Finalmente una vez que hemos arrancado nuestro servidor y simulado la llamada es momento de hacer un test para ver que todo funciona correctamente.
En nuestro caso en la respond hemos añadido withSuscess:
mockServer.expect(once(), requestTo("http://localhost:8081/accounts/579f32b8-9a31-470c-a40c-723ffdc21291")) .andRespond(withSuccess(objectMapper.writeValueAsString(account), MediaType.APPLICATION_JSON)); mockMvc.perform( MockMvcRequestBuilders.get(URI, "579f32b8-9a31-470c-a40c-723ffdc21291") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andExpect( content().json("{\"userId\":\"0060a6e2-f47e-42b5-8cb3-a45932920204\",\"accountNumber\":\"579f32b8-9a31-470c-a40c-723ffdc21291\",\"name\":\"noel\",\"surname\":null,\"address\":null,\"account\":{\"accountNumber\":\"0060a6e2-f47e-42b5-8cb3-a45932920204\",\"createdDate\":\"+999999999-12-31\",\"amount\":1.5}}"));
De modos que nuestra clase de test global quedaría de la siguiente manera.
@SpringBootTest(classes = { UserMockResttemplateApplication.class}, webEnvironment = WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc public class UserControllerIT { private static final String URI = "/users/{accountNumber}"; private MockRestServiceServer mockServer; @Autowired private RestTemplate restTemplate; @Autowired private ObjectMapper objectMapper; @Autowired private MockMvc mockMvc; @Autowired private UserRepository userRepository; @BeforeEach public void init() { mockServer = MockRestServiceServer.createServer(restTemplate); } @Test public void given_account_number_when_user_account_is_requested_then_user_account_is_returned() throws Exception { Account account = Account.builder() .accountNumber("0060a6e2-f47e-42b5-8cb3-a45932920204") .amount(new BigDecimal(1.5)) .createdDate(LocalDate.MAX).build(); UserEntity user = new UserEntity(); user.setUserId(UUID.fromString("0060a6e2-f47e-42b5-8cb3-a45932920204")); user.setAccountNumber("579f32b8-9a31-470c-a40c-723ffdc21291"); user.setName("noel"); userRepository.saveAndFlush(user); mockServer.expect(once(), requestTo("http://localhost:8081/accounts/579f32b8-9a31-470c-a40c-723ffdc21291")) .andRespond( withSuccess(objectMapper.writeValueAsString(account), MediaType.APPLICATION_JSON)); mockMvc.perform( MockMvcRequestBuilders.get(URI, "579f32b8-9a31-470c-a40c-723ffdc21291") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()).andExpect( content().json( "{\"userId\":\"0060a6e2-f47e-42b5-8cb3-a45932920204\",\"accountNumber\":\"579f32b8-9a31-470c-a40c-723ffdc21291\",\"name\":\"noel\",\"surname\":null,\"address\":null,\"account\":{\"accountNumber\":\"0060a6e2-f47e-42b5-8cb3-a45932920204\",\"createdDate\":\"+999999999-12-31\",\"amount\":1.5}}")); } }
Conclusión
En esta entrada hemos visto como podemos hacer Mock con RestTemplate en Spring, y aunque lo más normal que hoy se utilice algún cliente declarativo o reactivo, RestTemplate sigue teniendo mucho peso dentro de las aplicaciones.
Te puedes descargar el código en nuestro github.
Si necesitas más información puedes escribirnos un comentario o un correo electrónico a refactorizando.web@gmail.com o también nos puedes contactar por nuestras redes sociales Facebook o twitter y te ayudaremos encantados!