Class JuneauBeanStoreExtension
- All Implemented Interfaces:
org.junit.jupiter.api.extension.AfterAllCallback,org.junit.jupiter.api.extension.AfterEachCallback,org.junit.jupiter.api.extension.BeforeAllCallback,org.junit.jupiter.api.extension.BeforeEachCallback,org.junit.jupiter.api.extension.Extension,org.junit.jupiter.api.extension.ParameterResolver,org.junit.jupiter.api.extension.TestInstantiationAwareExtension
@TestBean-annotated members on a test class and exposes them as
a TestBeanStore overlay.
Mode INJECT — fresh-instance with overrides (default):
The extension supports two wiring patterns. Mode INJECT is the default and what every
@TestBean-based test ships with out of the box. The contract:
- The SUT (a
RestContextviaMockRestClient, aMicroservice, aSerializerSet, etc.) is constructed after the overlay is built. The overlay is threaded into the builder viaoverridingBeanStore(...)(seeBeanStoreOverridable), which installs it in theoverridingParentslot of the SUT's bean store at construction time. - Because the overlay sits at tier 1 of the resolution chain, every framework-managed bean lookup
consults it before any local
@Beanfactory or memoizer-backed default. Mode INJECT is therefore universal: all bean types are eligible for replacement, including those that subsequently get pinned into a per-op memoizer. - The overlay's lifetime is bounded by the SUT's lifetime. For a per-test fresh SUT (the most common
shape), the overlay is discarded when the SUT goes out of scope at
@AfterEach. For a per-class shared SUT (built once in@BeforeAll), the overlay survives until the SUT is torn down at@AfterAll. No explicit push/pop is required.
@TestBean, TestBeanStore,
BeanStoreOverridable, and the overridingBeanStore(...)
setters on MockRestClient.Builder, Microservice.Builder, SerializerSet.Builder,
ParserSet.Builder, and EncoderSet.Builder all implement this Mode INJECT contract.
Mode OVERLAY — existing-instance with push/pop:
Mode OVERLAY is the opt-in pattern for tests that hold a long-lived SUT (typically a
Microservice booted in @BeforeAll) and want to apply per-test overlays without rebuilding it.
The test calls attach(WritableBeanStore) to point the extension at the
SUT's bean store; the extension then uses
WritableBeanStore.pushOverlay(BeanStore)
at beforeEach / beforeAll and
WritableBeanStore.popOverlay(Snapshot)
at afterEach / afterAll so the SUT's bean store is restored between tests.
Mode OVERLAY requires @TestBean(mode = Mode.OVERLAY) on the participating annotations. Mixing
Mode.INJECT and Mode.OVERLAY on the same scope is rejected with a clear
IllegalStateException.
Overlay storage:
The extension owns one or two TestBeanStore overlays per test execution:
- A class-scope overlay built at
beforeAll, containing every@TestBean(scope = CLASS)declared onstaticfields/methods of the test class hierarchy. Lives untilafterAll. - A method-scope overlay built at
beforeEach, containing every@TestBean(scope = METHOD)declared on instance (or static, for convenience) fields/methods. The method-scope overlay's parent is set to the class-scope overlay (if any), so a method-scope override of(Type, name)shadows a class-scope override of the same pair while still letting the class-scope's other entries fall through. Lives untilafterEach.
Wiring into the system under test is explicit — the extension never reflects on the test class to
find a RestContext / Microservice field and never mutates the SUT after construction. Test
authors retrieve the current overlay via getStore() and plug it into the SUT's builder hook:
Two registration styles are supported:
- Declarative:
@ExtendWith(JuneauBeanStoreExtension.class)on the test class. - Programmatic: a
@RegisterExtensionfield initialized withcreate(), so the test can invokegetStore()from within@BeforeAll/@BeforeEachbodies as well as from test methods.
See Also:
TestBean- The annotation discovered by this extension.TestBeanStore- The overlay type the extension produces.Scope- Per-method vs per-class lifecycle selector.Mode- Mode INJECT (fresh instance) vs Mode OVERLAY (existing instance push/pop) selector.
- Since:
- 10.0.0
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordThe flattened result ofdiscoverOverrides(Object): the@TestBeanoverlay store, the unifiedModeof all declarations, and whether any were found.Nested classes/interfaces inherited from interface org.junit.jupiter.api.extension.TestInstantiationAwareExtension
org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidafterAll(org.junit.jupiter.api.extension.ExtensionContext context) voidafterEach(org.junit.jupiter.api.extension.ExtensionContext context) attach(WritableBeanStore store) Points this extension at a long-lived SUT'sWritableBeanStorefor Mode OVERLAY push/pop wiring.voidbeforeAll(org.junit.jupiter.api.extension.ExtensionContext context) voidbeforeEach(org.junit.jupiter.api.extension.ExtensionContext context) static JuneauBeanStoreExtensioncreate()Creates a new, unconfigured extension instance for programmatic registration via@RegisterExtension.detach()Clears theattachedbean store reference.discoverOverrides(Object testInstance) Discovers the@TestBeanoverrides declared on a test instance (and its class hierarchy) and returns them as a singleJuneauBeanStoreExtension.OverrideSet— the public composition entry point for other JUnit 5 extensions (e.g.Returns theattachedbean store, or empty if none.getStore()Returns the current effective overlay — method-scope if a test method is executing, otherwise the class-scope overlay.Returns the overlay for the specified scope, if one is currently active.resolveParameter(org.junit.jupiter.api.extension.ParameterContext parameterContext, org.junit.jupiter.api.extension.ExtensionContext extensionContext) booleansupportsParameter(org.junit.jupiter.api.extension.ParameterContext parameterContext, org.junit.jupiter.api.extension.ExtensionContext extensionContext) Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface org.junit.jupiter.api.extension.TestInstantiationAwareExtension
getTestInstantiationExtensionContextScope
-
Constructor Details
-
JuneauBeanStoreExtension
public JuneauBeanStoreExtension()Constructor.
-
-
Method Details
-
create
Creates a new, unconfigured extension instance for programmatic registration via@RegisterExtension.Equivalent to
new JuneauBeanStoreExtension()but reads more naturally at the registration site:@RegisterExtension JuneauBeanStoreExtensionext = JuneauBeanStoreExtension.create ();- Returns:
- A new extension instance. Never
null . - Since:
- 10.0.0
-
attach
Points this extension at a long-lived SUT'sWritableBeanStorefor Mode OVERLAY push/pop wiring.Call from a
@BeforeAllor@BeforeEachbody when the participating@TestBeandeclarations useMode.OVERLAY. Once attached, the extension will:pushthe class-scope overlay onto the attached store atbeforeAll(if a non-empty class-scope overlay exists), andpopit atafterAll.- Push the method-scope overlay at
beforeEachand pop it atafterEach.
Calling
attach(...)a second time beforedetach()is permitted — the most recent attachment wins. If never called, the extension stays in Mode INJECT.- Parameters:
store- The bean store to push/pop overlays against. Must not benull .- Returns:
- This extension, for fluent chaining.
- Throws:
NullPointerException- Ifstoreisnull .- Since:
- 10.0.0
-
detach
Clears theattachedbean store reference.Called automatically at
afterAll; tests may also call it explicitly between scenarios. Has no effect if the extension is not currently attached.- Returns:
- This extension, for fluent chaining.
- Since:
- 10.0.0
-
getAttachedStore
Returns theattachedbean store, or empty if none.Surface for diagnostic / testability purposes — the push/pop lifecycle uses the field directly.
- Returns:
- The attached store, or
Optional.empty()if not currently attached. - Since:
- 10.0.0
-
getStore
Returns the current effective overlay — method-scope if a test method is executing, otherwise the class-scope overlay.This is the accessor most tests plug into builder hooks such as
MockRestClient.Builder.overridingBeanStore(...).- Returns:
- The current overlay. Never
null . - Throws:
IllegalStateException- If no class-scope or method-scope overlay is currently active (e.g. the accessor is called from a@BeforeAllbody and the extension is not yet bound to a test class).- Since:
- 10.0.0
-
getStore
Returns the overlay for the specified scope, if one is currently active.Allows a test to reach past the current effective overlay and inspect a specific lifecycle layer. For example, a test running under both a class-scope and a method-scope overlay can use
getStore(Scope.CLASS)to retrieve the shared per-class layer directly.- Parameters:
scope- The lifecycle scope to query. Must not benull .- Returns:
- The overlay for that scope, or
Optional.empty()if no such overlay is active. - Since:
- 10.0.0
-
beforeAll
- Specified by:
beforeAllin interfaceorg.junit.jupiter.api.extension.BeforeAllCallback
-
afterAll
- Specified by:
afterAllin interfaceorg.junit.jupiter.api.extension.AfterAllCallback
-
beforeEach
- Specified by:
beforeEachin interfaceorg.junit.jupiter.api.extension.BeforeEachCallback
-
afterEach
- Specified by:
afterEachin interfaceorg.junit.jupiter.api.extension.AfterEachCallback
-
supportsParameter
public boolean supportsParameter(org.junit.jupiter.api.extension.ParameterContext parameterContext, org.junit.jupiter.api.extension.ExtensionContext extensionContext) - Specified by:
supportsParameterin interfaceorg.junit.jupiter.api.extension.ParameterResolver
-
resolveParameter
public Object resolveParameter(org.junit.jupiter.api.extension.ParameterContext parameterContext, org.junit.jupiter.api.extension.ExtensionContext extensionContext) - Specified by:
resolveParameterin interfaceorg.junit.jupiter.api.extension.ParameterResolver
-
discoverOverrides
Discovers the@TestBeanoverrides declared on a test instance (and its class hierarchy) and returns them as a singleJuneauBeanStoreExtension.OverrideSet— the public composition entry point for other JUnit 5 extensions (e.g.@MicroserviceTest) that need the@TestBeansubstrate without driving this extension's own lifecycle.Both
@TestBean(scope = CLASS)(static) and@TestBean(scope = METHOD)(instance) declarations are collected into one flattened overlay (method-scope chained over class-scope), so a composing extension that boots a single system-under-test per scope sees every declared override. The returnedJuneauBeanStoreExtension.OverrideSet.mode()is the unified mode of all declarations (defaulting toMode.INJECTwhen none are present); mixingINJECTandOVERLAYin one scope throwsIllegalStateException.- Parameters:
testInstance- The test-class instance to scan. Must not benull .- Returns:
- The discovered overrides + their unified mode. Never
null ;JuneauBeanStoreExtension.OverrideSet.isEmpty()istrue when no@TestBeanmembers were found. - Since:
- 10.0.0
-