Besides @Container test fields, how can @ServiceConnection be used with a container @Bean, and why does that matter for development-time?
answer
- @Bean + @ServiceConnection in @TestConfiguration
- Boot starts Startable container beans
- SpringApplication.from(...).with(...) for dev main
- One config: tests (@Import) + local run
- @ImportTestcontainers helper
basics
~20 sYou can define the container as a @Bean in a @TestConfiguration and annotate that bean with @ServiceConnection. Spring Boot manages its lifecycle. You can import the same config into a local main() to run the app against real containers during development.
solid answer
~40 sInstead of a static @Container field driven by the @Testcontainers JUnit extension, you can declare the container as a Spring @Bean (usually in a @TestConfiguration) annotated @ServiceConnection. Spring Boot's Testcontainers lifecycle initializer detects Startable container beans, starts them on context refresh and stops them on close, and derives ConnectionDetails just as with the field style. This bean-based approach powers Boot 3.1's 'Testcontainers at development time': you put container beans in src/test, then launch the app locally with a small test main that imports them (or via @ImportTestcontainers / SpringApplication.from(...).with(...)). The running dev app then talks to real Postgres/Kafka/etc. containers with no external setup. The same @TestConfiguration can be @Import-ed into integration tests, giving one shared container definition for both tests and local development.
code
java · 22 lines@TestConfiguration(proxyBeanMethods = false)
class ContainersConfig {
@Bean
@ServiceConnection
PostgreSQLContainer<?> postgres() {
return new PostgreSQLContainer<>("postgres:16");
}
}
// Reuse in an integration test
@SpringBootTest
@Import(ContainersConfig.class)
class OrderServiceIT { /* ... */ }
// Reuse to run the app locally against real containers
public class TestApp {
public static void main(String[] args) {
SpringApplication.from(MyApplication::main)
.with(ContainersConfig.class)
.run(args);
}
}go deeper
Not expected to know the @Bean/dev-time style.
Should know a container can be a @Bean and that Boot manages its lifecycle.
Should explain the @TestConfiguration + @Import pattern and SpringApplication.from().with() for local dev, plus the Startable lifecycle.
Should weigh sharing one container definition across tests and dev, container scoping/reuse, and keeping container beans out of the production jar.
## Two annotation targets `@ServiceConnection` can annotate either: 1. A **static `@Container` field** in a `@Testcontainers` test class (JUnit extension manages start/stop). 2. A **container `@Bean` method**, typically in a `@TestConfiguration`. ## The @Bean style ```java @TestConfiguration(proxyBeanMethods = false) class ContainersConfig { @Bean @ServiceConnection PostgreSQLContainer<?> postgres() { return new PostgreSQLContainer<>("postgres:16"); } @Bean @ServiceConnection(name = "redis") GenericContainer<?> redis() { return new GenericContainer<>("redis:7").withExposedPorts(6379); } } ``` Here there is **no `@Testcontainers`/`@Container`**. Instead Spring Boot's **`TestcontainersLifecycleApplicationContextInitializer`** recognizes beans implementing Testcontainers' **`Startable`** interface, **starts** them during context refresh and **stops** them when the context closes. `@ServiceConnection` on the bean derives `ConnectionDetails` exactly as before. Import into a test with `@Import(ContainersConfig.class)`. ## Development-time use (Boot 3.1 feature) Because the containers are just beans, you can run your **whole application locally** against them without installing databases. Boot generates/encourages a `TestApplication` main under `src/test`: ```java public class TestApp { public static void main(String[] args) { SpringApplication.from(MyApplication::main) .with(ContainersConfig.class) .run(args); } } ``` Running `TestApp.main` boots the real app, starts the containers, wires connections via `@ServiceConnection`, and gives you a fully working local environment. `@ImportTestcontainers` is a related helper for pulling in container definitions. ## Why it matters - **One definition, two uses**: the same `@TestConfiguration` serves integration tests (`@Import`) and local dev (`SpringApplication.from().with()`), avoiding drift between test and dev infra. - **No manual Docker setup** for developers; ephemeral, reproducible services. - Combined with Spring Boot DevTools, container reuse can keep restarts fast. ## Gotchas - The container **@Bean must be Startable** (Testcontainers containers are) so Boot can manage its lifecycle; a plain non-Startable object won't be started. - Bean-style containers are typically **singletons started once** per context; scope them carefully if you need isolation. - Still need the `name` hint for `GenericContainer` beans, same as the field style. - Container beans usually live in **`src/test`** (or a dev source set), not in production code, so they don't ship in the jar. - For tests, remember `@TestConfiguration` is not auto-scanned; you must `@Import` it (or use `@SpringBootTest(classes = ...)`).
- In the @Bean style, who starts and stops the container?Spring Boot's TestcontainersLifecycleApplicationContextInitializer detects Startable container beans and starts them on context refresh, stopping them when the context closes — no @Testcontainers extension needed.
- How do you run your app locally against these container beans?Write a test main that calls SpringApplication.from(MyApplication::main).with(ContainersConfig.class).run(args), so the real app boots with the containers started and wired via @ServiceConnection.
saying these in an interview costs you the question
- Thinking the @Bean style still needs @Testcontainers/@Container (it doesn't).
- Believing container beans belong in production source (they live in src/test).
- Forgetting @TestConfiguration must be @Import-ed, not auto-scanned.