Skip to content

Conversation

@codomposer
Copy link

@codomposer codomposer commented Nov 3, 2025

Description

This PR implements the Named Container Providers feature for JUnit Jupiter integration, addressing the need to share expensive container instances across multiple test classes without boilerplate code.

Fixes

Closes #6401

Problem

When running integration tests across multiple test classes, each class typically starts its own container instance, even when they could share the same container. This leads to:

  • Slow test execution (repeated container startup overhead)
  • Increased resource usage
  • Boilerplate code with manual singleton patterns

Solution

This PR introduces two new annotations that leverage the JUnit 5 extension API:

@ContainerProvider - Defines a named container factory method:

@ContainerProvider(name = "database", scope = Scope.GLOBAL)
public PostgreSQLContainer<?> createDatabase() {
    return new PostgreSQLContainer<>("postgres:14");
}

@ContainerConfig - References a container by name in tests:

@Test
@ContainerConfig(name = "database", injectAsParameter = true)
void testWithDatabase(PostgreSQLContainer<?> db) {
    // Container automatically started and injected
}

Key Features

  • Declarative container definition - No more static initializer blocks
  • Cross-class container sharing - Define once, use everywhere
  • Flexible scoping - Scope.CLASS (per test class) or Scope.GLOBAL (across all classes)
  • Type-safe parameter injection - Containers injected as test method parameters
  • Instance control - Choose between reusing containers or creating fresh instances
  • Backward compatible - Works alongside existing @Container fields
  • Comprehensive error handling - Clear error messages for common mistakes

Performance Impact

Before (without container reuse):

  • UserServiceTest: Start DB (5s) + Tests (2s) = 7s
  • OrderServiceTest: Start DB (5s) + Tests (2s) = 7s
  • PaymentServiceTest: Start DB (5s) + Tests (2s) = 7s
  • Total: 21 seconds

After (with container providers):

  • Start DB once (5s)
  • UserServiceTest: Tests (2s)
  • OrderServiceTest: Tests (2s)
  • PaymentServiceTest: Tests (2s)
  • Total: 11 seconds (48% faster!)

Example Usage

Base test class with shared providers:

abstract class BaseIntegrationTest {
    @ContainerProvider(name = "database", scope = Scope.GLOBAL)
    public PostgreSQLContainer<?> createDatabase() {
        return new PostgreSQLContainer<>("postgres:14");
    }
}

Test classes reusing the shared container:

@Testcontainers
class UserServiceTest extends BaseIntegrationTest {
    @Test
    @ContainerConfig(name = "database", injectAsParameter = true)
    void testCreateUser(PostgreSQLContainer<?> db) {
        // Uses shared database - no startup delay
    }
}

@Testcontainers
class OrderServiceTest extends BaseIntegrationTest {
    @Test
    @ContainerConfig(name = "database", injectAsParameter = true)
    void testCreateOrder(PostgreSQLContainer<?> db) {
        // Reuses the same database instance
    }
}

Changes

Core Implementation (4 new files)

  • ContainerProvider.java - Annotation for defining container providers
  • ContainerConfig.java - Annotation for referencing containers
  • ProviderMethod.java - Helper class for provider method metadata
  • ContainerRegistry.java - Registry managing container lifecycle

Modified Files (1 file)

  • TestcontainersExtension.java - Extended to support provider discovery, resolution, and parameter injection

Tests (9 new test files)

  • ContainerProviderBasicTests.java - Basic functionality
  • ContainerProviderParameterInjectionTests.java - Parameter injection
  • ContainerProviderNewInstanceTests.java - Instance control
  • ContainerProviderMultipleProvidersTests.java - Multiple providers
  • ContainerProviderScopeTests.java - Scope handling
  • ContainerProviderErrorHandlingTests.java - Error scenarios
  • ContainerProviderCrossClassTests.java - Cross-class sharing
  • ContainerProviderStaticMethodTests.java - Static methods
  • ContainerProviderMixedWithContainerTests.java - Backward compatibility
  • ContainerProviderRealWorldExampleTests.java - Real-world example

Documentation

  • Updated docs/test_framework_integration/junit_5.md with comprehensive guide
  • Added docs/examples/junit5/container-providers/README.md with examples
  • Added CONTAINER_PROVIDERS_FEATURE.md with implementation summary

Examples

  • Added practical examples in examples/container-providers/ demonstrating real-world usage

Testing

  • ✅ 9 test classes with 40+ test methods
  • ✅ Coverage: basic functionality, parameter injection, scoping, error handling, cross-class sharing
  • ✅ All tests pass with existing test suite
  • ✅ Backward compatibility verified with mixed usage tests

Backward Compatibility

This feature is fully backward compatible:

  • Existing @Container fields continue to work unchanged
  • Both approaches can coexist in the same test class
  • No breaking changes to existing API
  • No changes required to existing tests

Documentation

  • Comprehensive Javadoc for all new classes and methods
  • Updated JUnit 5 integration guide with "Named Container Providers" section
  • Real-world examples with 3 service integration test classes
  • Best practices and troubleshooting guide
  • Migration guide from manual singleton pattern

Checklist

  • New feature implemented with comprehensive tests
  • All existing tests pass
  • Documentation updated
  • Examples provided
  • Backward compatibility maintained
  • Code follows project style guidelines
  • Javadoc added for public APIs

Related Issues

This PR addresses the feature request for container reuse across multiple test classes, enabling:

  • Containers defined once and referenced by name
  • Automatic lifecycle management
  • Significant performance improvements for test suites with multiple integration test classes

Note: This implementation provides a clean, declarative alternative to the manual singleton pattern while maintaining full backward compatibility with existing code.

Contribution by Gittensor, learn more at https://gittensor.io/

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.

[Feature]: JUnit Jupiter extension for reuse of containers

1 participant