Repository: weld/weld-junit
Pull request #104 (2020-06-28 10:31:21)

describe @Nested limitation and inject into outer classes of @Nested inner test classes
see #103 

philippkunz: see #103 
manovotn: Fixes #103 
philippkunz: @manovotn, I went through all your comments and only noticed now at the bottom of the page that github tells me you only requested one change (so far) and not so many as I have now added even though that would have probably been visible at the top of the page, too.
After all that much natural language text in relation to the little quantity of code changed, I really hope I don't exceed your patience. I have no doubt you would have spent less time to complete the complete task than you have already spent with all my comments and are primarily helping me to get acquainted with the project and CDI...
Let me express that I really appreciate your review so far, not most your comments but all of them. Thank you. 
manovotn: > @manovotn, I went through all your comments and only noticed now at the bottom of the page that github tells me you only requested one change (so far)

I requested several, but not all were meant for immediate change. With GH review, you need to pick one of three options (approve, comment, request change) as an overall "frame" for all the code comments you add. So it can be misleading sometimes. But you answered all the comments, so all good :)

> After all that much natural language text in relation to the little quantity of code changed, I really hope I don't exceed your patience. I have no doubt you would have spent less time to complete the complete task than you have already spent with all my comments and are primarily helping me to get acquainted with the project and CDI...

That's ok. It isn't easy to understand because it spans over Weld and JUnit and on top of that we allow users to use several variants of extensions all of which behave slightly differently. Even I (as you can tell from the comments) don't remember every nook and cranny of why and how it was written.
manovotn: Ok, I *think* we have addressed all the comments at this point.
I will give it a second read once I have some free time next week, but I believe we should be good to merge this.

Patch:
diff --git a/junit5/README.md b/junit5/README.md
index 8488805..e2a10cd 100644
--- a/junit5/README.md
+++ b/junit5/README.md
@@ -382,18 +382,20 @@ class MyTest {
     }
 }
 ```
+
 ## WeldJunit5AutoExtension
 
 To use this approach, annotate your test class with `ExtendWith(WeldJunit5AutoExtension.class)` or just `@EnableAutoWeld`.
 By default, the extension will:
 
-* Inspect your test class and try to figure out what beans classes it needs based on injection points (field and parameter injection both work)
+* Inspect your test class and try to figure out what bean classes it needs based on injection points (field and parameter injection both work)
   * This is done by finding classes and verifying if they have [bean defining annotation](http://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#bean_defining_annotations) so make sure they do
 * Add those classes to Weld container
 * Process additional annotations on test class
   * `@AddPackages`, `@AddExtensions`, `@ActivateScopes`, ...
+* Annotates test classes with `@Singleton` and prevents another instantiation by Weld and instead substitutes the test instances provided by JUnit
 * Bootstrap Weld container
-* Inject into test instance, e.g. into all `@Inject` fields
+* Inject into test instances, e.g. into all `@Inject` fields
 * Inject into method parameters of your test methods
   * In case the type of the parameter matches a known and resolvable bean
   * By default, Weld is greedy and will try to resolve all parameters which are known as bean types in CDI container
@@ -575,3 +577,8 @@ This behaviour can be changed by setting a system property `org.jboss.weld.se.ar
 If set, Weld will use a _"flat"_ deployment structure - all bean classes share the same bean archive and all beans.xml descriptors are automatically merged into one.
 Thus alternatives, interceptors and decorators selected/enabled for a bean archive will be enabled for the whole application.
 Note that this configuration only makes difference if you run with *enabled discovery*; it won't affect your deployment if you use synthetic bean archive.
+
+
+## Limitations
+
+* `@Produces`, `@Disposes`, and `@Observes` don't work in `@Nested` test classes which fail to meet [valid bean](http://docs.jboss.org/cdi/spec/1.2/cdi-spec.html#what_classes_are_beans) requirements due to the lack of a no-arg constructor and Weld ignores them silently. However, `@Inject` and parameter injection also work with `@Nested` classes.
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/ExtensionContextUtils.java b/junit5/src/main/java/org/jboss/weld/junit5/ExtensionContextUtils.java
index bc9d337..ee14e41 100644
--- a/junit5/src/main/java/org/jboss/weld/junit5/ExtensionContextUtils.java
+++ b/junit5/src/main/java/org/jboss/weld/junit5/ExtensionContextUtils.java
@@ -16,9 +16,7 @@
  */
 package org.jboss.weld.junit5;
 
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 import org.jboss.weld.environment.se.WeldContainer;
 import org.junit.jupiter.api.extension.ExtensionContext;
@@ -36,12 +34,11 @@ public class ExtensionContextUtils {
     private static final String CONTAINER = "weldContainer";
     private static final String EXPLICIT_PARAM_INJECTION = "explicitParamInjection";
     private static final String WELD_ENRICHERS = "weldEnrichers";
-    private static final String INSTANCES_TO_INJECT_TO = "instancesToInjectTo";
 
     private static Namespace EXTENSION_NAMESPACE;
 
     // private constructor to prevent instantiation
-    private ExtensionContextUtils() {        
+    private ExtensionContextUtils() {
     }
 
     /**
@@ -83,34 +80,6 @@ public class ExtensionContextUtils {
         getTestStore(context).put(INITIATOR, initiator);
     }
 
-    /**
-     * For {@code @Nested} tests, this stores the enclosing class instance into root {@link ExtensionContext.Store}
-     * This has to be root store as each class and its nested classes have different {@link ExtensionContext}.
-     */
-    public static void setInstancesToInjectToToRootStore(ExtensionContext context, Object instanceToInjectTo) {
-        Set<Object> setOfObjects = getInstancesToInjectToFromRootStore(context);
-        if (setOfObjects == null) {
-            setOfObjects = new HashSet<>();
-        }
-        setOfObjects.add(instanceToInjectTo);
-        getRootExtensionStore(context).put(INSTANCES_TO_INJECT_TO, setOfObjects);
-    }
-
-    /**
-     * Root {@link ExtensionContext.Store} survives in between tests, we need to clear it after each test.
-     */
-    public static void clearInstancesToInjectToFromRootStore(ExtensionContext context) {
-        getRootExtensionStore(context).put(INSTANCES_TO_INJECT_TO, null);
-    }
-
-    /**
-     * For {@code @Nested} tests, this retrieves the enclosing class instances from root {@link ExtensionContext.Store}.
-     * This has to be root store as each class and its nested classes have different {@link ExtensionContext}.
-     */
-    public static Set<Object> getInstancesToInjectToFromRootStore(ExtensionContext context) {
-        return getRootExtensionStore(context).get(INSTANCES_TO_INJECT_TO, Set.class);
-    }
-
     /**
      * Return boolean indicating whether explicit parameter injection is enabled
      */
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/WeldInitiator.java b/junit5/src/main/java/org/jboss/weld/junit5/WeldInitiator.java
index 4eca1eb..9db13aa 100644
--- a/junit5/src/main/java/org/jboss/weld/junit5/WeldInitiator.java
+++ b/junit5/src/main/java/org/jboss/weld/junit5/WeldInitiator.java
@@ -17,7 +17,6 @@
 package org.jboss.weld.junit5;
 
 import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java b/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java
index d9e63a0..362cbd6 100644
--- a/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java
+++ b/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java
@@ -27,7 +27,6 @@ import org.junit.jupiter.api.extension.ExtensionContext;
 import org.junit.jupiter.api.extension.ParameterContext;
 import org.junit.jupiter.api.extension.ParameterResolutionException;
 import org.junit.jupiter.api.extension.ParameterResolver;
-import org.junit.jupiter.api.extension.TestInstancePostProcessor;
 
 import javax.enterprise.inject.spi.BeanManager;
 import java.lang.annotation.Annotation;
@@ -39,17 +38,13 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.ServiceLoader;
-import java.util.Set;
 import java.util.stream.Collectors;
 
-import static org.jboss.weld.junit5.ExtensionContextUtils.clearInstancesToInjectToFromRootStore;
 import static org.jboss.weld.junit5.ExtensionContextUtils.getContainerFromStore;
-import static org.jboss.weld.junit5.ExtensionContextUtils.getInstancesToInjectToFromRootStore;
 import static org.jboss.weld.junit5.ExtensionContextUtils.getEnrichersFromStore;
 import static org.jboss.weld.junit5.ExtensionContextUtils.getExplicitInjectionInfoFromStore;
 import static org.jboss.weld.junit5.ExtensionContextUtils.getInitiatorFromStore;
 import static org.jboss.weld.junit5.ExtensionContextUtils.setContainerToStore;
-import static org.jboss.weld.junit5.ExtensionContextUtils.setInstancesToInjectToToRootStore;
 import static org.jboss.weld.junit5.ExtensionContextUtils.setEnrichersToStore;
 import static org.jboss.weld.junit5.ExtensionContextUtils.setExplicitInjectionInfoToStore;
 import static org.jboss.weld.junit5.ExtensionContextUtils.setInitiatorToStore;
@@ -87,7 +82,7 @@ import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;
  * @see WeldJunitEnricher
  */
 public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback,
-        BeforeEachCallback, AfterEachCallback, ParameterResolver, TestInstancePostProcessor {
+        BeforeEachCallback, AfterEachCallback, ParameterResolver {
 
     // global system property
     public static final String GLOBAL_EXPLICIT_PARAM_INJECTION = "org.jboss.weld.junit5.explicitParamInjection";
@@ -114,8 +109,6 @@ public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback,
         if (determineTestLifecycle(context).equals(PER_CLASS)) {
             getInitiatorFromStore(context).shutdownWeld();
         }
-        // root context survives in between test classes, we need to clear information stored there
-        clearInstancesToInjectToFromRootStore(context);
     }
 
     @Override
@@ -137,8 +130,8 @@ public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback,
         }
     }
 
-    protected void weldInit(Object testInstance, ExtensionContext context, Weld weld, WeldInitiator.Builder weldInitiatorBuilder) {
-        weld.addPackage(false, testInstance.getClass());
+    protected void weldInit(ExtensionContext context, Weld weld, WeldInitiator.Builder weldInitiatorBuilder) {
+        weld.addPackage(false, context.getRequiredTestClass());
     }
 
     @Override
@@ -261,7 +254,7 @@ public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback,
                 Weld weld = WeldInitiator.createWeld();
                 WeldInitiator.Builder builder = WeldInitiator.from(weld);
 
-                weldInit(testInstance, context, weld, builder);
+                weldInit(context, weld, builder);
 
                 // Apply discovered enrichers
                 for (WeldJunitEnricher enricher : getEnrichersFromStore(context)) {
@@ -277,20 +270,11 @@ public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback,
 
             // this ensures the test class is injected into
             // in case of nested tests, this also injects into any outer classes
-            Set<Object> enclosingClasses = getInstancesToInjectToFromRootStore(context);
-            if (enclosingClasses != null) {
-                initiator.addObjectsToInjectInto(enclosingClasses);
-            }
+            initiator.addObjectsToInjectInto(context.getRequiredTestInstances().getAllInstances().stream().collect(Collectors.toSet()));
 
             // and finally, init Weld
             setContainerToStore(context, initiator.initWeld(testInstance));
         }
     }
 
-    @Override
-    public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
-        // in case of nested tests, this method gets invoked for the outer class and the inner class
-        // therefore we can use that to collect instances of objects we want to inject into
-        setInstancesToInjectToToRootStore(context, testInstance);
-    }
 }
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java b/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java
index 4e38768..5c02bc6 100644
--- a/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java
+++ b/junit5/src/main/java/org/jboss/weld/junit5/auto/ClassScanning.java
@@ -64,10 +64,10 @@ import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;
  */
 class ClassScanning {
 
-    static void scanForRequiredBeanClass(Class<?> testClass, Weld weld, boolean explicitInjection) {
+    static void scanForRequiredBeanClasses(List<Class<?>> testClasses, Weld weld, boolean explicitInjection) {
 
         List<Class<?>> classesToProcess = new ArrayList<>();
-        classesToProcess.add(testClass);
+        classesToProcess.addAll(testClasses);
 
         Set<Class<?>> foundClasses = new HashSet<>();
         Set<Type> excludedBeanTypes = new HashSet<>();
@@ -199,14 +199,10 @@ class ClassScanning {
 
         }
 
-        foundClasses.add(testClass);
-
         for (Class<?> foundClass : foundClasses) {
-
             if (hasBeanDefiningAnnotation(foundClass)) {
                 weld.addBeanClass(foundClass);
             }
-
         }
 
         weld.addExtension(new ExcludedBeansExtension(excludedBeanTypes, excludedBeanClasses));
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/auto/WeldJunit5AutoExtension.java b/junit5/src/main/java/org/jboss/weld/junit5/auto/WeldJunit5AutoExtension.java
index 2c1042b..02af769 100644
--- a/junit5/src/main/java/org/jboss/weld/junit5/auto/WeldJunit5AutoExtension.java
+++ b/junit5/src/main/java/org/jboss/weld/junit5/auto/WeldJunit5AutoExtension.java
@@ -25,6 +25,9 @@ import org.junit.platform.commons.support.AnnotationSupport;
 
 import static org.jboss.weld.junit5.ExtensionContextUtils.getExplicitInjectionInfoFromStore;
 
+import java.util.List;
+import java.util.stream.Collectors;
+
 /**
  * An alternative to {@link WeldJunit5Extension} allowing to fully leverage annotation based configuration approach.
  * When used, the extension will attempt to resolve all beans used in your test class and automatically adds them to
@@ -48,17 +51,24 @@ import static org.jboss.weld.junit5.ExtensionContextUtils.getExplicitInjectionIn
 public class WeldJunit5AutoExtension extends WeldJunit5Extension {
 
     @Override
-    protected void weldInit(Object testInstance, ExtensionContext context, Weld weld, WeldInitiator.Builder weldInitiatorBuilder) {
+    protected void weldInit(ExtensionContext context, Weld weld, WeldInitiator.Builder weldInitiatorBuilder) {
 
-        Class<?> testClass = testInstance.getClass();
+        List<?> testInstances = context.getRequiredTestInstances().getAllInstances();
+        List<Class<?>> testClasses = testInstances.stream().map(Object::getClass).collect(Collectors.toList());
 
-        ClassScanning.scanForRequiredBeanClass(testClass, weld, getExplicitInjectionInfoFromStore(context));
+        ClassScanning.scanForRequiredBeanClasses(testClasses, weld, getExplicitInjectionInfoFromStore(context));
 
-        weld.addBeanClass(testClass);
-        weld.addExtension(new TestInstanceInjectionExtension(testClass, testInstance));
+        // Add the outer-most test class only because Weld would ignore inner, @Nested test classes anyway
+        // due to their not meeting valid beans requirements for not having a no-arg constructor.
+        // Note that getAllInstances above returns the tests "ordered from outermost to innermost".
+        Object outermostTestInstance = testInstances.get(0);
+        weld.addBeanClasses(outermostTestInstance.getClass());
+        weld.addExtension(new TestInstanceInjectionExtension<>(outermostTestInstance));
 
-        AnnotationSupport.findRepeatableAnnotations(testClass, ActivateScopes.class)
-                .forEach(ann -> weldInitiatorBuilder.activate(ann.value()));
+        testClasses.stream()
+                .map(testClass -> AnnotationSupport.findRepeatableAnnotations(testClass, ActivateScopes.class))
+                .flatMap(ann -> ann.stream().map(ActivateScopes::value))
+                .forEach(weldInitiatorBuilder::activate);
 
     }
 


Test patch:
diff --git a/junit5/src/main/java/org/jboss/weld/junit5/auto/TestInstanceInjectionExtension.java b/junit5/src/main/java/org/jboss/weld/junit5/auto/TestInstanceInjectionExtension.java
index 41cb3db..2fd6a92 100644
--- a/junit5/src/main/java/org/jboss/weld/junit5/auto/TestInstanceInjectionExtension.java
+++ b/junit5/src/main/java/org/jboss/weld/junit5/auto/TestInstanceInjectionExtension.java
@@ -16,124 +16,69 @@
  */
 package org.jboss.weld.junit5.auto;
 
-import org.jboss.weld.util.annotated.ForwardingAnnotatedType;
-
 import javax.enterprise.context.spi.CreationalContext;
 import javax.enterprise.event.Observes;
-import javax.enterprise.inject.spi.AnnotatedType;
 import javax.enterprise.inject.spi.BeanManager;
 import javax.enterprise.inject.spi.Extension;
-import javax.enterprise.inject.spi.InjectionPoint;
 import javax.enterprise.inject.spi.InjectionTarget;
 import javax.enterprise.inject.spi.ProcessAnnotatedType;
 import javax.enterprise.inject.spi.ProcessInjectionTarget;
 import javax.enterprise.util.AnnotationLiteral;
 import javax.inject.Singleton;
-import java.lang.annotation.Annotation;
-import java.util.Set;
-import java.util.stream.Collectors;
 
+import org.jboss.weld.injection.ForwardingInjectionTarget;
 
 /**
- * Extension class that ensure proper injection of an externally provided
- * test instance.
- *
- * NOTE: When JUnit 5 provides a test instance creation extension point this
- * should be removed and the test instance should be obtained from the
- * container via proper CDI methods.
- * https://github.com/junit-team/junit5/issues/672
+ * Extension that makes a test instance appear like a regular bean even though instantiated by JUnit.
+ * Injection into all test instances, also {@link org.junit.jupiter.api.Nested &#064;Nested} ones, is handled in {@link org.jboss.weld.junit5.WeldInitiator#addObjectsToInjectInto} and related.
+ * Proper handling of all other CDI annotations such as {@link javax.enterprise.inject.Produces &#064;Produces} is supported only on top level test classes.
  */
-public class TestInstanceInjectionExtension implements Extension {
+public class TestInstanceInjectionExtension<T> implements Extension {
 
     private static final AnnotationLiteral<Singleton> SINGLETON_LITERAL = new AnnotationLiteral<Singleton>() {};
 
-    private static final class AnnotationRewritingAnnotatedType<T> extends ForwardingAnnotatedType<T> {
-
-        private AnnotatedType<T> delegate;
-        private Set<Annotation> annotations;
-
-        public AnnotationRewritingAnnotatedType(AnnotatedType<T> delegate, Set<Annotation> annotations) {
-            this.delegate = delegate;
-            this.annotations = annotations;
-        }
-
-        @Override
-        public AnnotatedType<T> delegate() {
-            return delegate;
-        }
-
-        @Override
-        public Set<Annotation> getAnnotations() {
-            return annotations;
-        }
-
-    }
-
     private Class<?> testClass;
-    private Object testInstance;
+    private T testInstance;
 
-    TestInstanceInjectionExtension(Class<?> testClass, Object testInstance) {
-        this.testClass = testClass;
+    TestInstanceInjectionExtension(T testInstance) {
+        this.testClass = testInstance.getClass();
         this.testInstance = testInstance;
     }
 
-    <T> void rewriteTestClassScope(@Observes ProcessAnnotatedType<T> pat, BeanManager beanManager) {
-
-        AnnotatedType<T> annotatedType = pat.getAnnotatedType();
+    void rewriteTestClassScope(@Observes ProcessAnnotatedType<T> pat, BeanManager beanManager) {
 
-        if (annotatedType.getJavaClass().equals(testClass)) {
-
-            // Replace any test class's scope with @Singleton
-            Set<Annotation> annotations = annotatedType.getAnnotations().stream()
-                    .filter(annotation -> beanManager.isScope(annotation.annotationType()))
-                    .collect(Collectors.toSet());
-            annotations.add(SINGLETON_LITERAL);
-
-            pat.setAnnotatedType(new AnnotationRewritingAnnotatedType<>(annotatedType, annotations));
+        if (pat.getAnnotatedType().getJavaClass().equals(testClass)) {
+            pat.configureAnnotatedType().add(SINGLETON_LITERAL);
         }
-    }
-
-    <T> void rewriteTestClassInjections(@Observes ProcessInjectionTarget<T> pit) {
 
-        if (pit.getAnnotatedType().getJavaClass().equals(testClass)) {
-
-            InjectionTarget<T> wrapped = pit.getInjectionTarget();
+    }
 
-            pit.setInjectionTarget(new InjectionTarget<T>() {
+    private class TestInstanceInjectionTarget extends ForwardingInjectionTarget<T> {
 
-                @SuppressWarnings("unchecked")
-                @Override
-                public T produce(CreationalContext<T> creationalContext) {
-                    return (T) testInstance;
-                }
+        private InjectionTarget<T> injectionTarget;
 
-                @Override
-                public void dispose(T t) {
-                    wrapped.dispose(t);
-                }
+        TestInstanceInjectionTarget(InjectionTarget<T> injectionTarget) {
+            this.injectionTarget = injectionTarget;
+        }
 
-                @Override
-                public Set<InjectionPoint> getInjectionPoints() {
-                    return wrapped.getInjectionPoints();
-                }
+        @Override
+        protected InjectionTarget<T> delegate() {
+            return injectionTarget;
+        }
 
-                @Override
-                public void inject(T t, CreationalContext<T> creationalContext) {
-                    wrapped.inject(t, creationalContext);
-                }
+        @Override
+        public T produce(CreationalContext<T> creationalContext) {
+            return testInstance;
+        }
 
-                @Override
-                public void postConstruct(T t) {
-                    wrapped.postConstruct(t);
-                }
+    }
 
-                @Override
-                public void preDestroy(T t) {
-                    wrapped.preDestroy(t);
-                }
-            });
+    void rewriteTestInstanceInjectionTarget(@Observes ProcessInjectionTarget<T> pit) {
 
+        if (pit.getAnnotatedType().getJavaClass().equals(testClass)) {
+            pit.setInjectionTarget(new TestInstanceInjectionTarget(pit.getInjectionTarget()));
         }
+
     }
 
 }
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/nested/OuterTestClassAndBeanSameInstanceTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/nested/OuterTestClassAndBeanSameInstanceTest.java
new file mode 100644
index 0000000..5c6d2a2
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/nested/OuterTestClassAndBeanSameInstanceTest.java
@@ -0,0 +1,152 @@
+package org.jboss.weld.junit5.auto.nested;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Objects;
+
+import javax.enterprise.context.Dependent;
+import javax.enterprise.event.Event;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.Any;
+import javax.enterprise.inject.Disposes;
+import javax.enterprise.inject.Produces;
+import javax.inject.Inject;
+
+import org.jboss.weld.junit5.auto.EnableAutoWeld;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests that outer test classes don't get instantiated again for CDI annotations.
+ * <p>
+ * Classes with CDI annotations are usually treated as beans by Weld and, hence, instantiated as and when required. Test classes, however, are instantiated by JUnit.
+ * Weld should pick up the test instances instantiated by JUnit instead of instantiating another instance and continue to operate on them as usual with beans.
+ * <br>
+ * This test asserts that the same instance as the {@link org.junit.jupiter.api.Test &#064;Test} methods are invoked on
+ * <ul>
+ * <li>gets {@link Inject &#064;Inject}ed</li>
+ * <li>and used to invoke methods
+ * <ul>
+ * <li>annotated with {@link Produces &#064;Produces}, or</li>
+ * <li>with a parameter annotated with
+ * <ul>
+ * <li>{@link Observes &#064;Observes} or</li>
+ * <li>{@link Disposes &#064;Disposes}</li>
+ * </ul></li></ul></li></ul>
+ * and assumes if that were not true that there would have been another instance of the test class.
+ * <p>
+ * In order to understand how the test works (or failed earlier), consider to with particular test instance each outer test class member variable would refer to
+ * if Weld instantiated another, second, and undesired instance of the outer test class besides the obviously wanted one already instantiated by JUnit.
+ * <p>
+ * Besides all that, Weld ignores classes that don't meet valid bean requirements which affects all {@link org.junit.jupiter.api.Nested &#064;Nested} test classes due to a lack of a no-arg constructor and
+ * Weld is reluctant to handle {@link Produces &#064;Produces}, {@link Observes &#064;Observes}, or {@link Disposes &#064;Disposes} annotations on them or
+ * instances of {@link Inject &#064;Inject}ing nested test classes into other beans
+ * but {@link Inject &#064;Inject}ing into test instances works on all test classes whether {@link org.junit.jupiter.api.Nested &#064;Nested} or not.
+ * 
+ * @see <a href="https://github.com/weld/weld-junit/issues/103">https://github.com/weld/weld-junit/issues/103</a>
+ */
+@EnableAutoWeld
+public class OuterTestClassAndBeanSameInstanceTest {
+
+    interface Bean {
+        default String ping() {
+            // use hashCode to verify same instance in assertSame thereby ignoring proxies
+            return "ping" + Objects.hashCode(this);
+        }
+    }
+
+    static void assertSameBean(Bean bean1, Bean bean2) {
+        assertEquals(bean1.ping(), bean2.ping());
+    }
+
+    Bean theBean = new Bean() {
+    };
+
+    @Nested
+    class NestedInjectEnclosingTestInstanceDirectlyTest {
+
+        @Inject
+        OuterTestClassAndBeanSameInstanceTest testInstanceInjected;
+
+        @Test
+        void test() {
+            assertSameBean(theBean, testInstanceInjected.theBean);
+        }
+
+        @Test
+        void test(OuterTestClassAndBeanSameInstanceTest outerTestInstanceAsParameter) {
+            assertSameBean(theBean, outerTestInstanceAsParameter.theBean);
+        }
+
+    }
+
+    @Dependent
+    static class TestInstanceInjected {
+
+        @Inject
+        OuterTestClassAndBeanSameInstanceTest outerTestInstance;
+
+    }
+
+    @Nested
+    class NestedInjectEnclosingTestInstanceIndirectlyTest {
+
+        @Inject
+        TestInstanceInjected testInstanceInjected;
+
+        @Test
+        void test() {
+            assertSameBean(theBean, testInstanceInjected.outerTestInstance.theBean);
+        }
+
+        @Test
+        void test(TestInstanceInjected testInstanceInjectedAsParameter) {
+            assertSameBean(theBean, testInstanceInjectedAsParameter.outerTestInstance.theBean);
+        }
+
+    }
+
+    @Produces
+    Bean produceBean() {
+        return theBean;
+    }
+
+    void disposeBean(@Disposes Bean bean) {
+        assertEquals(theBean.ping(), bean.ping());
+    }
+
+    @Inject @Any
+    Event<Bean> event;
+
+    Bean observedBean;
+
+    void observeBean(@Observes Bean bean) {
+        assertSameBean(theBean, bean);
+        observedBean = theBean;
+    }
+
+    @Nested
+    class NestedProducesDisposesObservesTest {
+
+        @Inject
+        Bean nestedInjectBean;
+
+        @Test
+        void test() {
+            assertSameBean(theBean, nestedInjectBean);
+        }
+
+        @Test
+        void test(Bean nestedInjectBeanParameter) {
+            assertSameBean(theBean, nestedInjectBeanParameter);
+        }
+
+        @Test
+        void testEvent() {
+            event.fire(nestedInjectBean);
+            assertSameBean(theBean, observedBean);
+        }
+
+    }
+
+}