## Description Migrate 17 Testcontainers guides from testcontainers.com into the Docker docs site, covering Java (14 guides), .NET (2 guides), and Node.js (1 guide). This follows up on PR #24450 which added the initial Go and Python guides. Each guide is converted from AsciiDoc to Hugo Markdown, split into multi-chapter stepper navigation, updated to the latest Testcontainers API, and verified with passing tests running in containers. Java guides use testcontainers-java 2.0.4 with the new 2.x Maven coordinates and package names (e.g., `testcontainers-postgresql`, `org.testcontainers.postgresql.PostgreSQLContainer`). The Quarkus guide uses Quarkus 3.22.3 with TC 1.x managed by the Quarkus BOM, since no released Quarkus version ships TC 2.x yet. ## How to test All code snippets have been verified by running each guide's source repository tests inside Docker containers with the Docker socket mounted. To re-run the verification, use the `/testcontainers-guides-migrator` skill included in this PR (`.claude/skills/testcontainers-guides-migrator/SKILL.md`). The skill's Step 6 documents the exact container commands and macOS Docker Desktop workarounds (host override, docker-java API version, etc.) needed to run each language's tests: ``` /testcontainers-guides-migrator I want you to verify all the guides in this branch. Do a full review, verifying that all code snippets compile, the code is executable, and ALL the tests pass. Run them as docker containers, never locally. ``` ## Related issues or tickets Supersedes #24450 (expanded from 2 guides to all 19) ## Reviews - [ ] Technical review - [ ] Editorial review - [ ] Product review --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9.5 KiB
title, linkTitle, description, weight
| title | linkTitle | description | weight |
|---|---|---|---|
| Create the Spring Boot project | Create the project | Set up a Spring Boot OAuth 2.0 Resource Server with Keycloak, PostgreSQL, and Testcontainers. | 10 |
Set up the project
Create a Spring Boot project from Spring Initializr by selecting the Spring Web, Validation, JDBC API, PostgreSQL Driver, Spring Security, OAuth2 Resource Server, and Testcontainers starters.
Alternatively, clone the guide repository.
After generating the application, add the testcontainers-keycloak community module and REST Assured as test dependencies.
The key dependencies in pom.xml are:
<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-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</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-postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.dasniko</groupId>
<artifactId>testcontainers-keycloak</artifactId>
<version>3.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Create the domain model
Create a Product record that represents the domain object:
package com.testcontainers.products.domain;
import jakarta.validation.constraints.NotEmpty;
public record Product(Long id, @NotEmpty String title, String description) {}
Create the repository
Implement ProductRepository using Spring JdbcClient to interact with a
PostgreSQL database:
package com.testcontainers.products.domain;
import java.util.List;
import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
@Repository
public class ProductRepository {
private final JdbcClient jdbcClient;
public ProductRepository(JdbcClient jdbcClient) {
this.jdbcClient = jdbcClient;
}
public List<Product> getAll() {
return jdbcClient.sql("SELECT * FROM products").query(Product.class).list();
}
public Product create(Product product) {
String sql =
"INSERT INTO products(title, description) VALUES (:title,:description) RETURNING id";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcClient
.sql(sql)
.param("title", product.title())
.param("description", product.description())
.update(keyHolder);
Long id = keyHolder.getKeyAs(Long.class);
return new Product(id, product.title(), product.description());
}
}
Add a schema creation script
Create src/main/resources/schema.sql to initialize the products table:
CREATE TABLE products (
id bigserial primary key,
title varchar not null,
description text
);
Enable schema initialization in src/main/resources/application.properties:
spring.sql.init.mode=always
For production applications, use a database migration tool like Flyway or Liquibase instead.
Implement the API endpoints
Create ProductController with endpoints to fetch all products and create a
product:
package com.testcontainers.products.api;
import com.testcontainers.products.domain.Product;
import com.testcontainers.products.domain.ProductRepository;
import jakarta.validation.Valid;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/products")
class ProductController {
private final ProductRepository productRepository;
ProductController(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@GetMapping
List<Product> getAll() {
return productRepository.getAll();
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
Product createProduct(@RequestBody @Valid Product product) {
return productRepository.create(product);
}
}
Configure OAuth 2.0 security
Create a SecurityConfig class that protects the API endpoints using JWT
token-based authentication:
package com.testcontainers.products.config;
import static org.springframework.security.config.Customizer.withDefaults;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(c ->
c
.requestMatchers(HttpMethod.GET, "/api/products")
.permitAll()
.requestMatchers(HttpMethod.POST, "/api/products")
.authenticated()
.anyRequest()
.authenticated()
)
.sessionManagement(c ->
c.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.cors(CorsConfigurer::disable)
.csrf(CsrfConfigurer::disable)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults()));
return http.build();
}
}
This configuration:
- Permits unauthenticated access to
GET /api/products. - Requires authentication for
POST /api/productsand all other endpoints. - Configures the OAuth 2.0 Resource Server with JWT token-based authentication.
- Disables CORS and CSRF because this is a stateless API.
Add the JWT issuer URI to application.properties:
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9090/realms/keycloaktcdemo
Export the Keycloak realm configuration
Before writing the tests, export a Keycloak realm configuration so that the test environment can import it automatically. Start a temporary Keycloak instance:
$ docker run -p 9090:8080 \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:25 start-dev
Open http://localhost:9090 and sign in to the Admin Console with admin/admin.
Then set up the realm:
- In the top-left corner, select the realm drop-down and create a realm named
keycloaktcdemo. - Under the
keycloaktcdemorealm, create a client with the following settings:- Client ID:
product-service - Client Authentication: On
- Authentication flow: select only Service accounts roles
- Client ID:
- On the Client details screen, go to the Credentials tab and copy the Client secret value.
Export the realm configuration:
$ docker ps
# copy the keycloak container id
$ docker exec -it <container-id> /bin/bash
$ /opt/keycloak/bin/kc.sh export --dir /opt/keycloak/data/import --realm keycloaktcdemo
$ exit
$ docker cp <container-id>:/opt/keycloak/data/import/keycloaktcdemo-realm.json keycloaktcdemo-realm.json
Copy the exported keycloaktcdemo-realm.json file into src/test/resources.