Skip to main content

JUnit Extensions

The juneau-bct module bundles a small JUnit 5 extension for test-time bean injection — swapping production beans with test doubles inside a Juneau BeanStore for the duration of a JUnit 5 test, without modifying production code and without proxying. It is the Juneau-flavored answer to Spring's @TestConfiguration / @MockBean.

The extension lives in the org.apache.juneau.bct.junit package and centers on three types:

  • JuneauBeanStoreExtension — the JUnit 5 Extension, registered via @ExtendWith or @RegisterExtension.
  • TestBean — the field/method annotation that declares an override.
  • TestBeanStore — the overlay bean store, resolvable as a test-method parameter.

Quick start​

import static org.junit.jupiter.api.Assertions.*;

import org.apache.juneau.bct.junit.*;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.*;

@ExtendWith(JuneauBeanStoreExtension.class)
class MyServiceTest {

@TestBean
ExternalApi mockApi = () -> "test-double";

@Test
void resolvesTheTestDouble(TestBeanStore store) {
assertSame(mockApi, store.getBean(ExternalApi.class).orElseThrow());
}

interface ExternalApi { String describe(); }
}

Two wiring modes​

  • Mode INJECT (default) — construction-time wiring. The system-under-test is built after the overlay, which is threaded into the builder's overridingBeanStore(...) setter. Universal: every bean type is eligible for replacement, including ones the framework would otherwise pin into a per-op memoizer at boot.
  • Mode OVERLAY — push/pop overlays on a long-lived SUT (typically a Microservice booted once in @BeforeAll), applied and removed per test via @TestBean(mode = Mode.OVERLAY).

The overridingBeanStore(BeanStore) setter is available on every builder implementing BeanStoreOverridable<B>: MockRestClient.Builder, Microservice.Builder, SerializerSet.Builder, ParserSet.Builder, and EncoderSet.Builder.

Reference​

@TestBean attributes​

AttributeDefaultDescription
name""Bean qualifier name; empty means unnamed.
typeObject.classBean type to register under. When left as Object.class the field's declared type (or the method's return type) is used.
scopeScope.METHODScope.METHOD (fresh overlay per test method) or Scope.CLASS (shared overlay for the whole class; only valid on static members).
modeMode.INJECTMode.INJECT or Mode.OVERLAY (see above). Mixing both modes within one scope throws IllegalStateException.

@TestBean may be placed on fields or on methods (the method is invoked to supply the bean). Method-scope overlays are stacked on top of the class-scope overlay, so a method-scope bean takes precedence over a class-scope bean of the same type.

Registering the extension​

// Declarative
@ExtendWith(JuneauBeanStoreExtension.class)
class MyTest { ... }

// Programmatic, when you need the extension instance
class MyTest {
@RegisterExtension
static JuneauBeanStoreExtension ext = JuneauBeanStoreExtension.create();
}

JuneauBeanStoreExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback and ParameterResolver.

MethodDescription
create()Static factory for @RegisterExtension.
attach(WritableBeanStore) / detach()Attach the overlay to (or detach it from) a long-lived store, as used by Mode OVERLAY.
getAttachedStore()The attached store, as an Optional.
getStore()The active overlay for the current test method or class.
getStore(Scope)The overlay for the given scope, as an Optional<TestBeanStore>.
discoverOverrides(Object)Static; scans a test instance (and its hierarchy) for @TestBean members and returns the overrides.

TestBeanStore​

TestBeanStore extends BasicBeanStore and has no JUnit dependency. It can be created with new TestBeanStore() or new TestBeanStore(BeanStore parent) and can be declared as a test-method parameter to receive the current overlay.

MethodDescription
override(Class<T>, T)Register an unnamed override.
override(Class<T>, T, String)Register a named override.
override(Class<T>, Supplier<T>)Register a lazily supplied override.
override(Class<T>, Supplier<T>, String)Register a lazily supplied, named override.

REST-focused deep dive​

For a full walkthrough of @TestBean with MockRestClient — named-bean qualifiers, explicit-type overrides, per-test vs. per-class scope, and the Mode-OVERLAY safety inventory — see Test-time Bean Injection.