When Mocks Aren't Enough
Auto-mocking gets the container started. But sometimes a Mockito mock isn't what you need — you want a real implementation that records calls, returns computed values, or validates inputs.
CDI has @Alternative for exactly this. With
@Priority, they're globally enabled — which works
fine and the addon doesn't interfere with that. But a globally
enabled alternative affects every test on the classpath.
Sometimes you need different replacements for different tests.
The Solution: @TestBean
@TestBean activates @Alternative beans
per test class, without @Priority.
The extension enables them programmatically and vetoes unselected
alternatives to prevent conflicts. Existing
Other alternatives — whether enabled via @Priority
or beans.xml — continue to work normally as long
as their bean types don't overlap with a @TestBean
replacement. The extension respects @Typed on both
sides when checking for type clashes.
-
Create your replacement as a normal CDI bean with
@Alternative— define your own scope, qualifiers, and logic. Don't add@Priority.@Alternative @ApplicationScoped public class CustomGreeting implements Greeting { @Override public String greet(String name) { return "Hello, " + name + "!"; } } -
Annotate your test class with
@EnableTestBeans+@TestBeanto activate it. The annotation is repeatable — use multiple@TestBeanwhen you need a few replacements. For larger setups, bundle them into composable meta-annotations.@EnableTestBeans @TestBean(bean = CustomGreeting.class) class GreetingTest { // Greeting injection points now use CustomGreeting // Everything else is still auto-mocked } -
Activate multiple replacements
—
@TestBeanis repeatable:@EnableTestBeans @TestBean(bean = CustomGreeting.class) @TestBean(bean = CustomNotificationSender.class) class FullIntegrationTest { // Both alternatives active, rest auto-mocked } -
Or use an inline
@TestBeanfield — define and stub a Mockito mock right in the test class:@EnableTestBeans class GreetingTest { @TestBean private static Greeting greeting = Mockito.mock(Greeting.class); @Inject GreetingConsumer consumer; @Test void stubbedMockIsInjected() { Mockito.when(greeting.greet("world")).thenReturn("Stubbed!"); assertEquals("Stubbed!", consumer.getGreeting().greet("world")); assertNull(consumer.getGreeting().greet("other")); // unstubbed → null } }No separate class, no
@Alternative, no@ApplicationScoped. The static field holds a pre-configured Mockito mock that becomes a CDI bean. Stubbing and verification work exactly as you'd expect.
For producer beans with @Produces methods, composable
meta-annotations, whitelist mode, and more, see
Advanced Features.