Crear el proyecto Spring Boot
Configurar el proyecto
Crea un proyecto Spring Boot desde Spring Initializr seleccionando los starters Spring Web, Spring Reactive Web y Testcontainers.
Alternativamente, puedes clonar el repositorio de la guía.
Después de generar el proyecto, agrega las bibliotecas REST Assured y MockServer
como dependencias de prueba. Las dependencias clave en pom.xml son:
<properties>
<java.version>17</java.version>
<testcontainers.version>2.0.4</testcontainers.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-mockserver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<version>5.15.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>Se recomienda utilizar el BOM (Bill of Materials) de Testcontainers para que no tengas que repetir la versión en cada dependencia de módulo de Testcontainers.
Esta guía construye una aplicación que gestiona álbumes de video. Una API REST de terceros maneja los recursos de fotos. Con fines de demostración, la aplicación utiliza la API pública JSONPlaceholder como servicio de fotos.
La aplicación expone un endpoint GET /api/albums/{albumId} que llama al servicio
de fotos para obtener las fotos de un álbum específico.
MockServer es una biblioteca para simular servicios
basados en HTTP. Testcontainers proporciona un
módulo de MockServer que
ejecuta MockServer como un contenedor Docker.
Crear los modelos Album y Photo
Crea Album.java utilizando Java records:
package com.testcontainers.demo;
import java.util.List;
public record Album(Long albumId, List<Photo> photos) {}
record Photo(Long id, String title, String url, String thumbnailUrl) {}Crear la interfaz PhotoServiceClient
Spring Framework 6 introdujo soporte para clientes HTTP declarativos. Crea una interfaz con un método que obtenga las fotos para un ID de álbum determinado:
package com.testcontainers.demo;
import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.service.annotation.GetExchange;
interface PhotoServiceClient {
@GetExchange("/albums/{albumId}/photos")
List<Photo> getPhotos(@PathVariable Long albumId);
}Registrar PhotoServiceClient como un bean
Para generar una implementación en tiempo de ejecución de PhotoServiceClient,
regístralo como un bean de Spring utilizando HttpServiceProxyFactory. La fábrica
requiere una implementación de HttpClientAdapter. Spring Boot proporciona
WebClientAdapter como parte de la biblioteca spring-webflux:
package com.testcontainers.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.support.WebClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;
@Configuration
public class AppConfig {
@Bean
public PhotoServiceClient photoServiceClient(
@Value("${photos.api.base-url}") String photosApiBaseUrl
) {
WebClient client = WebClient.builder().baseUrl(photosApiBaseUrl).build();
HttpServiceProxyFactory factory = HttpServiceProxyFactory
.builder(WebClientAdapter.forClient(client))
.build();
return factory.createClient(PhotoServiceClient.class);
}
}La URL base del servicio de fotos se externaliza como una propiedad de
configuración. Agrega la siguiente entrada a
src/main/resources/application.properties:
photos.api.base-url=https://jsonplaceholder.typicode.comCrear el endpoint de la API REST
Crea AlbumController.java:
package com.testcontainers.demo;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClientResponseException;
@RestController
@RequestMapping("/api")
class AlbumController {
private static final Logger logger = LoggerFactory.getLogger(
AlbumController.class
);
private final PhotoServiceClient photoServiceClient;
AlbumController(PhotoServiceClient photoServiceClient) {
this.photoServiceClient = photoServiceClient;
}
@GetMapping("/albums/{albumId}")
public ResponseEntity<Album> getAlbumById(@PathVariable Long albumId) {
try {
List<Photo> photos = photoServiceClient.getPhotos(albumId);
return ResponseEntity.ok(new Album(albumId, photos));
} catch (WebClientResponseException e) {
logger.error("Failed to get photos", e);
return new ResponseEntity<>(e.getStatusCode());
}
}
}Este endpoint llama al servicio de fotos para un ID de álbum específico y devuelve una respuesta como:
{
"albumId": 1,
"photos": [
{
"id": 51,
"title": "non sunt voluptatem placeat consequuntur rem incidunt",
"url": "https://via.placeholder.com/600/8e973b",
"thumbnailUrl": "https://via.placeholder.com/150/8e973b"
},
{
"id": 52,
"title": "eveniet pariatur quia nobis reiciendis laboriosam ea",
"url": "https://via.placeholder.com/600/121fa4",
"thumbnailUrl": "https://via.placeholder.com/150/121fa4"
}
]
}