Skip to content

feat(junit-jupiter): add @SharedContainers for cross-class container sharing#11709

Closed
kimjonghun0157 wants to merge 5 commits intotestcontainers:mainfrom
kimjonghun0157:feature/shared-containers-annotation
Closed

feat(junit-jupiter): add @SharedContainers for cross-class container sharing#11709
kimjonghun0157 wants to merge 5 commits intotestcontainers:mainfrom
kimjonghun0157:feature/shared-containers-annotation

Conversation

@kimjonghun0157
Copy link
Copy Markdown

Problem

When multiple test classes extend a common base class, @Testcontainers restarts containers for each class. This happens because TestcontainersExtension stores containers in a class-scoped ExtensionContext.Store, so even static @Container fields are stopped and restarted at every class boundary.

Resolves #1441

Solution

Introduce @SharedContainers and SharedContainersExtension that store static @Container fields in the root ExtensionContext.Store (context.getRoot().getStore(...)). The root store persists for the entire JVM session, so containers are started at most once and cleaned up automatically via CloseableResource / Ryuk at JVM shutdown.

// Before: containers restart for each test class
@Testcontainers
abstract class BaseTest {
    @Container
    static final PostgreSQLContainer<?> DB = new PostgreSQLContainer<>("postgres:16");
}

// After: container starts once, shared across all subclasses
@SharedContainers
abstract class BaseTest {
    @Container
    static final PostgreSQLContainer<?> DB = new PostgreSQLContainer<>("postgres:16");
}

Changes

File Description
SharedContainers.java New annotation (disabledWithoutDocker, parallel options)
SharedContainersExtension.java Extension storing containers in root store; signalAfterTestToContainersFor() helper consistent with TestcontainersExtension
SharedContainersBaseTest.java Abstract base for cross-class sharing integration tests
SharedContainersTestClassA/B.java Verify same container ID across two test classes
SharedContainersExtensionTests.java Unit tests for disabledWithoutDocker / Docker availability
SharedContainersLifecycleAwareTest.java Verify beforeTest()/afterTest() callbacks on TestLifecycleAware containers
docs/test_framework_integration/junit_5.md Document new annotation with usage example

Notes

  • Instance fields (@Container non-static) still restart per test method, identical to @Testcontainers
  • @SharedContainers and @Testcontainers should not be mixed on the same class hierarchy
  • Parallel execution support follows the same limitations as @Testcontainers (sequential only)

🤖 Generated with Claude Code

kimjonghun0157 and others added 5 commits April 10, 2026 11:57
…sharing

Introduce @SharedContainers annotation and SharedContainersExtension to
enable static @container fields to be shared across multiple test classes
in the same JVM session.

The core issue with @testcontainers is that containers are stored in a
class-scoped ExtensionContext.Store, causing them to stop and restart for
each test class even when declared as static fields on a shared base class.

SharedContainersExtension solves this by storing containers in the root
ExtensionContext.Store (context.getRoot().getStore()), which persists for
the entire JVM session. Containers are started at most once and cleaned up
automatically at JVM shutdown via Ryuk or JUnit's CloseableResource.

Resolves: testcontainers#1441

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add package-private isDockerAvailable() hook to SharedContainersExtension
  so that unit tests can control Docker availability without a real daemon,
  following the same pattern as TestcontainersExtension
- Add SharedContainersExtensionTests covering disabledWithoutDocker=true/false
  scenarios via a subclass that overrides isDockerAvailable()
- Document @SharedContainers in junit_5.md under a new
  "Shared containers across test classes" section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ontainers

SharedContainersExtension was missing AfterAllCallback, so containers
implementing TestLifecycleAware never received afterTest() after each
test class completed.

Fix by:
- Adding AfterAllCallback to the implements list
- Storing lifecycle-aware containers in the class-level store in beforeAll()
- Implementing afterAll() to retrieve and signal afterTest() to them

This matches the behaviour of TestcontainersExtension for shared containers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update SharedContainersExtensionTests to use the doReturn(...).when(...) syntax for mocking ExtensionContext. This approach is generally preferred over when(...).thenReturn(...) to avoid unintended side effects or type-safety issues during stubbing.
…r and add lifecycle test

Extract duplicate afterAll/afterEach signal logic into signalAfterTestToContainersFor()
helper method, consistent with TestcontainersExtension pattern.

Add SharedContainersLifecycleAwareTest to verify beforeTest()/afterTest() callbacks
are correctly signalled to TestLifecycleAware shared containers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Better mechanism to define singleton containers

1 participant