Technical Internals
How the Extension Works Under the Hood

CDI lifecycle hooks, JUnit integration, alternative isolation, and the mock bean implementation.

Auto-Mocking: CDI Lifecycle Hooks

The extension uses three CDI lifecycle events to discover unsatisfied injection points and fill them with Mockito mocks.

1. Collecting injection points

<T> void collectInjectionPoints(@Observes ProcessInjectionPoint<?, T> pip) {
    // Records the type and qualifiers of every injection point
}

<T> void collectFromBean(@Observes ProcessBean<T> pb) {
    // Catches inherited generic injection points that some
    // implementations miss in ProcessInjectionPoint
}

The ProcessBean observer is critical for OpenWebBeans compatibility — OWB does not always fire ProcessInjectionPoint for inherited generic fields like BaseDao<E> in an abstract superclass.

2. Registering beans and mocks

During AfterBeanDiscovery, the extension registers (in order):

  1. Test class as a @Singleton bean (if addTestClass = true)
  2. Inline @Produces @TestBean fields from the active test class — static field values become @Singleton beans
  3. Mockito mocks for remaining unsatisfied injection points (skipping types already covered by inline fields, @TestBean alternatives, or third-party extensions)

MockBean properties

PassivationCapable

Unique ID per bean type, satisfying Weld's requirement for normal-scoped beans.

Qualifier-Aware

Carries exact qualifiers from the injection point. Follows CDI spec rules for @Default.

@Nonbinding Support

Qualifier comparison uses reflection to skip @Nonbinding members.

@Named Support

Returns the @Named value as the bean name when present.

@TestBean: Alternative Isolation

When @EnableTestBeans is present, the extension adds three more CDI lifecycle phases before auto-mocking:

Phase -2: Force discovery of scope-less alternatives

During BeforeBeanDiscovery, if a @TestBean-referenced class has @Alternative but no scope annotation, the extension calls addAnnotatedType() to force CDI to discover it in bean-discovery-mode="annotated", then adds @Dependent as the default scope.

Phase -1: Veto conflicting alternatives

During ProcessAnnotatedType, the extension checks each @Alternative bean against the @TestBean set. Bean types are resolved respecting @Typed on both the candidate and the selected alternatives:

In whitelist mode (limitToTestBeans = true), ALL application beans are vetoed except the whitelisted ones.

Phase 0: Enable selected alternatives

During AfterTypeDiscovery, the extension reads @TestBean annotations from the active test class — recursively walking meta-annotations — and enables each referenced alternative via getAlternatives().add().

No @Priority needed. Alternatives are enabled programmatically per container bootstrap. Different test classes can activate different alternatives for the same type without conflicts. Other alternatives — whether enabled via @Priority or beans.xml — work normally as long as their bean types don't overlap with a @TestBean replacement.

JUnit Integration

@EnableTestBeans carries @ExtendWith for the internal JUnit extension. The lifecycle:

JUnit PhaseAction
beforeAll Sets active test class in DynamicTestBeanContext. If manageContainer = true, starts the CDI SE container.
beforeEach Activates request scope via RequestContextController.
postProcessTestInstance Injects @Inject fields on the test instance using BeanManager.getInjectionTargetFactory().
afterEach Deactivates request scope.
afterAll Shuts down the CDI container. Resets DynamicTestBeanContext.

Meta-Annotation Resolution

@TestBean declarations are collected recursively from the test class's annotation hierarchy. The algorithm:

  1. Read all annotations on the test class
  2. For each @TestBean or @TestBeans, collect the bean and beanProducer references
  3. For each non-JDK/non-CDI annotation, recurse into its meta-annotations
  4. Track visited annotation types to prevent infinite loops
  5. Collect into a Set<Class<?>> — duplicates are automatically deduplicated

Third-Party Extension Compatibility

The extension detects types managed by third-party CDI extensions and skips mocking them. Currently recognized:

Package Structure

PackageContents
org.os890.cdi.addon.dynamictestbean Public API@EnableTestBeans, @TestBean, @TestBeans
org.os890.cdi.addon.dynamictestbean.internal Extension internals — DynamicTestBeanExtension, DynamicTestBeanJUnitExtension, DynamicTestBeanContext