Dynamic CDI Test Bean Addon
Auto-Mock Unsatisfied CDI Injection Points

A CDI portable extension that fills missing beans with Mockito mocks at bootstrap. One test dependency. Zero configuration.

CDI 4.1 Mockito 5 JUnit 6 Java 25+ Apache License 2.0

The Problem

Multi-module CDI projects fail to start in isolated module tests:

WELD-001408: Unsatisfied dependencies for type BaseDao<DummyEntity>
  at injection point [BackedAnnotatedField] @Inject private BaseService.dao

Your module injects interfaces or abstract types whose implementations live in a different module. Production assembles everything. Your isolated module test doesn't.

The Fix

  1. Add one test dependency:
    <dependency>
        <groupId>org.os890.cdi.addon</groupId>
        <artifactId>dynamic-cdi-test-bean-addon-cdi</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <scope>test</scope>
    </dependency>

    The API (@EnableTestBeans, @TestBean) is included transitively. Using Quarkus/ArC instead? See the Quarkus page for the matching artifact.

  2. A typical bean in your common module:
    // common/src/main/java
    @ApplicationScoped
    public class BaseService<E extends BaseEntity> {
    
        @Inject
        private BaseDao<E> dao;          // abstract — impls in app modules
    
        @Inject
        private UserContext userContext;  // interface — impl in auth module
    
        public boolean isReady() {
            return dao != null;
        }
    
        public String getEntityName() {
            // ... derived from E
        }
    
        public E findById(long id) {
            return dao.findById(id);
        }
    }
    Without the addon, both injection points fail:
    BaseDao<E> — abstract class, concrete implementations exist only in app modules.
    UserContext — interface defined in common, but the implementation lives in the auth module.
    The addon auto-mocks both so the container starts.
  3. Test it — without a database or auth module:
    // common/src/test/java
    
    @EnableTestBeans
    class BaseServiceTest {
    
        @Inject
        BaseService<DummyEntity> service;  // real bean in common
    
        @Test
        void serviceStartsWithoutAppModules() {
            // BaseService is a real common-module bean.
            // BaseDao and UserContext have no impl here —
            // the extension auto-mocked both with Mockito.
            assertNotNull(service);
            assertTrue(service.isReady());
        }
    
        @Test
        void serviceLogicWorksWithoutDao() {
            // Logic that doesn't touch the DAO works normally.
            assertEquals("DummyEntity", service.getEntityName());
        }
    
        @Test
        void daoCallsReturnMockitoDefaults() {
            // Calls that go through the mocked DAO return null.
            assertNull(service.findById(1L));
        }
    }

BaseService<E> is a real bean in common. It injects BaseDao<E> (abstract — concrete implementations live in app modules) and UserContext (just an interface — the auth module provides the implementation). Both are auto-mocked with Mockito so you can test the service's own logic in isolation. No @BeforeAll, no SeContainerInitializer, no manual mock wiring. Need a real UserContext instead of a mock? See @TestBean.

[DynamicTestBean] Registering mock for: BaseDao<DummyEntity>
[DynamicTestBean] Registering mock for: UserContext
[DynamicTestBean] Registered 2 mock bean(s) for unsatisfied injection points.

What's Next

Auto-mocking gets the container started. When you need more control:

Built on the standard CDI API only. Works with OpenWebBeans, Weld, and Quarkus/ArC. No vendor lock-in. Compatible with DeltaSpike. Tested with 128 tests across 3 modules on every build.
Using Claude Code? A custom skill is included. Copy it to .claude/skills/cdi-test/SKILL.md in your project and use /cdi-test to generate CDI tests with auto-mocking and @TestBean support.