Class AnnotatedTestFieldSetup<A extends Annotation,T>
- Type Parameters:
A- annotation typeT- required field type for auto validations (could be Object)
- All Implemented Interfaces:
TestExecutionListener,TestEnvironmentSetup
- Direct Known Subclasses:
LogFieldsSupport,MockFieldsSupport,RestStubFieldsSupport,SpyFieldsSupport,StubFieldsSupport,TrackerFieldsSupport,WebClientFieldsSupport,WebResourceClientFieldsSupport
Encapsulates:
- fields detection and processing
- declaration validations
- nested tests support (avoid duplicate fields initialization)
- manual user values support
- injected field lifecycle support (if required to reset an object before each test)
The overall process is: find all annotated fields, request supporting object initialization (field value might be provided by user), request a field value object to inject into test (not calling if already initialized).
Each field is represented by a self-contained object
(AnnotatedField), which could hold an additional managing
object to avoid external state requirement (all related objects already presented in field instance - no need for
an external state).
It is recommended to encapsulate the main logic into a separate hook (a hook object would perform required application modifications). Setup object should only provide the required configuration to this hook. This should greatly simplify the extension and make it more testable.
All methods are protected to simplify overriding logic if required.
Implementation could be registered as usual TestEnvironmentSetup
object (hook and listener would be registered automatically).
- Since:
- 10.02.2025
-
Field Summary
FieldsModifier and TypeFieldDescriptionprotected booleanTrue if guicey extension start application per test class (for all methods).protected static final StringIndicates injected field value (or pre-initialized value presence).protected static final StringIndicates user-provided value (field was pre-initialized by user).protected List<AnnotatedField<A,T>> Resolved annotated fields.protected Class<?>Test class.protected org.junit.jupiter.api.extension.ExtensionContextJunit context, used for fields search.protected StringHuman-readable (if @DisplayName or spock used) class or method name. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidafterEach(EventContext context) Called after each test method execution.protected abstract voidafterTest(EventContext context, AnnotatedField<A, T> field, T value) Called after each test to post-process field value (if required).voidbeforeAll(EventContext context) IMPORTANT: this method MIGHT NOT BE CALLED at all in case if extension is registered under non-static field (and so application created before each method).voidbeforeEach(EventContext context) Called before each test method execution.protected abstract voidbeforeTest(EventContext context, AnnotatedField<A, T> field, T value) Called before each test to pre-process field value (if required).protected abstract voidbeforeValueInjection(EventContext context, AnnotatedField<A, T> field) Called after application startup and before field value injection.protected voidfailIfInstanceFieldInitialized(AnnotatedField<A, T> field, org.junit.jupiter.api.extension.TestInstances testInstances) When guicey extension starts in beforeAll - it can't see instance fields (by default) and so can't check if use provide any value.protected abstract voidfieldDetected(org.junit.jupiter.api.extension.ExtensionContext context, AnnotatedField<A, T> field) Validate resolved field, if required.protected org.junit.jupiter.api.extension.ExtensionContextgetClassContext(org.junit.jupiter.api.extension.ExtensionContext context) Class context is the same for all test methods, and so it is suitable for storing something that must survive between test methods.protected StringgetDeclarationErrorPrefix(AnnotatedField<A, T> field) protected List<AnnotatedField<A,T>> getOwnFields(org.junit.jupiter.api.extension.ExtensionContext context) protected List<AnnotatedField<A,T>> getParentFields(org.junit.jupiter.api.extension.ExtensionContext context) This is important for the nested tests - each nested test may have its own set of fields (if guicey extension created per test method).protected org.junit.jupiter.api.extension.ExtensionContext.StoregetStore(org.junit.jupiter.api.extension.ExtensionContext context) Gets test-specific extension storage.protected abstract <K> voidinitializeField(AnnotatedField<A, T> field, T userValue) Configure application for a field (user value might be provided).protected abstract TinjectFieldValue(EventContext context, AnnotatedField<A, T> field) Get test field value (would be immediately injected into the test field).protected voidinjectValues(EventContext context, List<AnnotatedField<A, T>> fields, org.junit.jupiter.api.extension.TestInstances testInstances) Inject field values into test instance (under beforeEach).protected booleanisInstanceBinding(com.google.inject.Binding<?> binding) Note: covers only the simplest cases (just for a basic validations).protected List<AnnotatedField<A,T>> lookupFields(org.junit.jupiter.api.extension.ExtensionContext context, jakarta.inject.Provider<List<AnnotatedField<A, T>>> fieldsProvider) Resolve test own fields or use already resolved fields set (for example, when guicey extension created for each method we can search and validate fields just once).protected abstract voidregisterHooks(TestExtension extension) Called to register additional guicey hooks, if required.protected abstract voidreport(EventContext context, List<AnnotatedField<A, T>> fields) Called when debug is enabled on guicey extension to report registered fields.setup(TestExtension extension) Called before test application startup under junit "before all" phase or "before each" (depends on extension registration).voidstarted(EventContext context) Called when dropwizard (or guicey) application started.voidstarting(EventContext context) Called before dropwizard (or guicey) application starting.voidstopped(EventContext context) Called when dropwizard (or guicey) application stopped.protected voidvalidateUnreachableFieldsInNestedTest(Class<?> testClass) When guicey extension created in beforeAll, same extension would be used for nested tests, which means that, if nested test declares any annotated fields, they can't be injected into already started guice context and so usage error must be reported.protected voidvalueLifecycle(EventContext context, List<AnnotatedField<A, T>> fields, org.junit.jupiter.api.extension.TestInstances testInstances, boolean before) Called in beforeEach/afterEach to apply automatic lifecycle for field objects.Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface ru.vyarus.dropwizard.guice.test.jupiter.env.listen.TestExecutionListener
afterAll, stopping
-
Field Details
-
FIELD_MANUAL
Indicates user-provided value (field was pre-initialized by user).- See Also:
-
FIELD_INJECTED
Indicates injected field value (or pre-initialized value presence).- See Also:
-
appPerClass
protected boolean appPerClassTrue if guicey extension start application per test class (for all methods). -
regTestClass
Test class. -
setupContextName
Human-readable (if @DisplayName or spock used) class or method name. -
fields
Resolved annotated fields. -
setupContext
protected org.junit.jupiter.api.extension.ExtensionContext setupContextJunit context, used for fields search. Required for reporting (because the report would be generated on the different phase).
-
-
Constructor Details
-
AnnotatedTestFieldSetup
On extending, use a default constructor and specify required parameters manually.- Parameters:
fieldAnnotation- field annotation classfieldType- required fields type (could be Object)storageKey- key used to store a fields list in junit context (must be unique for extension)
-
-
Method Details
-
setup
Description copied from interface:TestEnvironmentSetupCalled before test application startup under junit "before all" phase or "before each" (depends on extension registration). Assumed to be used for starting additional test objects (like embedded database) and application configuration (configuration overrides).Provided object allow you to provide direct configuration overrides (e.g. to override database credentials).For simplicity, any non closable returned object simply ignored. This was done to simplify lambas usage:
TestEnvironmentSetup env = ext -> ext.configOverrides("foo:1")- here configuration object would be implicitly returned (because all methods return object itself for chained calls) and ignored.- Specified by:
setupin interfaceTestEnvironmentSetup- Parameters:
extension- test extension configuration object (support chained calls)- Returns:
AutoCloseableorExtensionContext.Store.CloseableResourceif something needs to be shut down after test, any other object would be ignored (including null)
-
fieldDetected
protected abstract void fieldDetected(org.junit.jupiter.api.extension.ExtensionContext context, AnnotatedField<A, T> field) Validate resolved field, if required. Note that some validations are performed automatically like checking field type with provided required type or unreachable annotated fields reporting. This method should be used for validations, which are not possible to perform automatically (e.g., there is a class, declared in annotation that must comply with a field type (base class know nothing about annotation and can't check that).Called only for current test class own fields: in case of nested test, root test fields would already be validated. Also, if guice context started per each test method, validation would be called only for the first test method because fields would be searched just once - no need to validate each time.
- Parameters:
context- junit contextfield- annotated fields
-
registerHooks
Called to register additional guicey hooks, if required. Called only when at least one annotated field is detected.- Parameters:
extension- extension configuration object
-
initializeField
Configure application for a field (user value might be provided). There might be field object instance creation (e.g. mocks initialization), guice overrides registration, etc. The main initialization point.NOTE: If user-provided values are not allowed, throw an exception here
- Type Parameters:
K- type for aligning a binding key with value types (cheating on guice type checks)- Parameters:
field- annotated fielduserValue- user-provided field value (pre-initialized)
-
beforeValueInjection
Called after application startup and before field value injection. Useful for additional validations, which can't be performed before, like binding correctness validation (requiring the created injector): for example, to detect instance bindings when extension relies on AOP and so would not work. Such validation is impossible to do before (in time of binding overrides).Called before
injectFieldValue(ru.vyarus.dropwizard.guice.test.jupiter.env.listen.EventContext, AnnotatedField). At this point, non-static fields could be resolved (test instance present).Important: method called for all fields, even initialized by user! Inject value method might not be called after it!
- Parameters:
context- event contextfield- annotated field
-
injectFieldValue
Get test field value (would be immediately injected into the test field). Called only if field was not initialized by user (not a manual value). For example, implementation might simply get bean instance from guice context (if guice was re-configured with module overrides).Warning: not called for manually initialized fields (because value already set)! To validate binding use
beforeValueInjection(ru.vyarus.dropwizard.guice.test.jupiter.env.listen.EventContext, AnnotatedField)method instead (which is called for all fields).Application already completely started and test extension initialized at this moment (beforeEach test phase).
- Parameters:
context- event contextfield- annotated field- Returns:
- created field value
-
report
Called when debug is enabled on guicey extension to report registered fields. Note: there might be fields from multiple test classes in case of nested tests.Report called after application startup because at this point all fields were processed (in configure guice method) and so all required fields data collected. Called only if at least one field is detected.
Special custom data markers used in field objects (
AnnotatedField.getCustomData(String)):FIELD_MANUALfield value was initialized by user, otherwise automaticFIELD_INJECTEDfield injection instance (test instance)
- Parameters:
context- event context, IMPORTANT - this would be setup context and not currentfields- fields to report
-
beforeTest
Called before each test to pre-process field value (if required).- Parameters:
context- event contextfield- filed descriptorvalue- value instance
-
afterTest
Called after each test to post-process field value (if required).- Parameters:
context- event contextfield- filed descriptorvalue- value instance
-
starting
Description copied from interface:TestExecutionListenerCalled before dropwizard (or guicey) application starting. It could be beforeAll or beforeEach phase (if important, lookExtensionContext.getTestMethod()to make sure). Application could start/stop multiple times within one test class (if extension registered in non-static field).NOTE: At this stage, injections not yet performed inside test instance.
This method could be used instead of beforeAll because normally extension is created under beforeAll, but for extensions created under beforeEach - it would be impossible to notify about beforeAll anyway.
- Specified by:
startingin interfaceTestExecutionListener- Parameters:
context- context object providing access to all available objects (junit context, test support, etc.)- Throws:
Exception- on error
-
started
Description copied from interface:TestExecutionListenerCalled when dropwizard (or guicey) application started. It could be beforeAll or beforeEach phase (if important, lookExtensionContext.getTestMethod()to make sure). Application could start/stop multiple times within one test class (if extension registered in non-static field).NOTE: At this stage, injections not yet performed inside test instance.
This method could be used instead of beforeAll because normally extension is created under beforeAll, but for extensions created under beforeEach - it would be impossible to notify about beforeAll anyway.
- Specified by:
startedin interfaceTestExecutionListener- Parameters:
context- context object providing access to all required objects (junit context, injector, test support, etc.)
-
beforeAll
Description copied from interface:TestExecutionListenerIMPORTANT: this method MIGHT NOT BE CALLED at all in case if extension is registered under non-static field (and so application created before each method). PreferTestExecutionListener.started(ru.vyarus.dropwizard.guice.test.jupiter.env.listen.EventContext)instead, which is always called (but not always under beforeAll),Method could be useful if some action must be performed before each test (in case of nested tests or global application when "start" would not be called for each test).
- Specified by:
beforeAllin interfaceTestExecutionListener- Parameters:
context- context object providing access to all required objects (junit context, injector, test support, etc.)
-
beforeEach
Description copied from interface:TestExecutionListenerCalled before each test method execution. Guice injections into test instance already performed. Even if an application is created in beforeEach phase, this method would be called after application creation.- Specified by:
beforeEachin interfaceTestExecutionListener- Parameters:
context- context object providing access to all required objects (junit context, injector, test support, etc.)
-
afterEach
Description copied from interface:TestExecutionListenerCalled after each test method execution. Even if an application is closed on afterEach, this method would be called before it.- Specified by:
afterEachin interfaceTestExecutionListener- Parameters:
context- context object providing access to all required objects (junit context, injector, test support, etc.)
-
stopped
Description copied from interface:TestExecutionListenerCalled when dropwizard (or guicey) application stopped. It could be afterAll or afterEach phase (if important, lookExtensionContext.getTestMethod()to make sure). Application could start/stop multiple times within one test class (if extension registered in non-static field).Note that in case of global application usage or for nested tests this method might not be called because application lifecycle would be managed by the top-most test.
This method could be used instead of afterAll because normally extension is stopped under afterAll, but for extensions stopped under afterEach - it would be impossible to notify about afterAll anyway.
- Specified by:
stoppedin interfaceTestExecutionListener- Parameters:
context- context object providing access to all required objects (junit context, injector, test support, etc.)
-
lookupFields
protected List<AnnotatedField<A,T>> lookupFields(org.junit.jupiter.api.extension.ExtensionContext context, jakarta.inject.Provider<List<AnnotatedField<A, T>>> fieldsProvider) Resolve test own fields or use already resolved fields set (for example, when guicey extension created for each method we can search and validate fields just once). Also adds fields from all paren contexts (for nested tests, which must see parent test fields and so we need to manage its lifecycle)- Parameters:
context- junit contextfieldsProvider- fields searching logic- Returns:
- all annotated fields
-
injectValues
protected void injectValues(EventContext context, List<AnnotatedField<A, T>> fields, org.junit.jupiter.api.extension.TestInstances testInstances) Inject field values into test instance (under beforeEach). User defined values stay as is. Value is injected only once for test instance.- Parameters:
context- event contextfields- annotated fieldstestInstances- tests instances (might be several for nested tests)
-
valueLifecycle
protected void valueLifecycle(EventContext context, List<AnnotatedField<A, T>> fields, org.junit.jupiter.api.extension.TestInstances testInstances, boolean before) Called in beforeEach/afterEach to apply automatic lifecycle for field objects.- Parameters:
context- junit contextfields- annotated fieldstestInstances- test instances (might be several for nested tests)before- true for beforeEach, false for afterEach
-
getDeclarationErrorPrefix
- Parameters:
field- field- Returns:
- universal prefix for field declaration errors
-
failIfInstanceFieldInitialized
protected void failIfInstanceFieldInitialized(AnnotatedField<A, T> field, org.junit.jupiter.api.extension.TestInstances testInstances) When guicey extension starts in beforeAll - it can't see instance fields (by default) and so can't check if use provide any value. On beforeEach we have to validate that value was not provided, because it's too late - guice context was already created.- Parameters:
field- field to checktestInstances- test instances (might be several in case of nested tests)
-
validateUnreachableFieldsInNestedTest
When guicey extension created in beforeAll, same extension would be used for nested tests, which means that, if nested test declares any annotated fields, they can't be injected into already started guice context and so usage error must be reported.- Parameters:
testClass- nested test class
-
getParentFields
protected List<AnnotatedField<A,T>> getParentFields(org.junit.jupiter.api.extension.ExtensionContext context) This is important for the nested tests - each nested test may have its own set of fields (if guicey extension created per test method). When guicey extension is created for nested test, we still need to resolve parent test fields to correctly initialize them and apply value lifecycle (because nested test could see parent fields).- Parameters:
context- junit context- Returns:
- all fields in parent contexts
-
getOwnFields
protected List<AnnotatedField<A,T>> getOwnFields(org.junit.jupiter.api.extension.ExtensionContext context) - Parameters:
context- junit context- Returns:
- fields which belong to test class (ignoring fields in parent contexts)
-
getStore
protected org.junit.jupiter.api.extension.ExtensionContext.Store getStore(org.junit.jupiter.api.extension.ExtensionContext context) Gets test-specific extension storage.- Parameters:
context- junit context (class level!)- Returns:
- extension storage object
-
getClassContext
protected org.junit.jupiter.api.extension.ExtensionContext getClassContext(org.junit.jupiter.api.extension.ExtensionContext context) Class context is the same for all test methods, and so it is suitable for storing something that must survive between test methods.- Parameters:
context- junit context- Returns:
- class junit context
-
isInstanceBinding
protected boolean isInstanceBinding(com.google.inject.Binding<?> binding) Note: covers only the simplest cases (just for a basic validations).- Parameters:
binding- binding- Returns:
- true if guice does not manage bean instance, false otherwise
-