Repository: weld/weld-junit
Pull request #112 (2021-01-08 10:17:04)

Find Bean Classes In Inherited '@Inject'-ed Field
I wondered why I had to specify `@AddBeanClass` with a class the same test inherited an injected field of that type. The bean class would have been found and added automatically if the injected field would have been on the test class itself as opposed to inherited. Weld also makes no such difference on whether injected fields are inherited or not so I figure `weld-junit` should follow the same rules with `@EnableAutoWeld` mode as well.

My guess is that there was an error in `ClassScanning`'s method formerly named `findAnnotatedFields` and now renamed to `findAnnotatedDeclaredFields` having called `getDeclaredFields` which excludes inherited fields but the name of that method formerly named `findAnnotatedFields` did not reflect this restriction and was missed where it is used in `scanForRequiredBeanClasses`.

Producers, however, are not inherited, see https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#member_level_inheritance.

philippkunz: https://github.com/weld/weld-junit/issues/114

Patch:
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 362cbd6..99cab46 100644
--- a/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java
+++ b/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java
@@ -186,7 +186,7 @@ public class WeldJunit5Extension implements AfterAllCallback, BeforeAllCallback,
     }
 
     private TestInstance.Lifecycle determineTestLifecycle(ExtensionContext ec) {
-        // check the test for import org.junit.jupiter.api.TestInstance annotation
+        // check the test for org.junit.jupiter.api.TestInstance annotation
         TestInstance annotation = ec.getRequiredTestClass().getAnnotation(TestInstance.class);
         if (annotation != null) {
             return annotation.value();
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 5c02bc6..5ed52a4 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
@@ -86,7 +86,7 @@ class ClassScanning {
 
             foundClasses.add(currClass);
 
-            findAnnotatedFields(currClass, ExcludeBean.class).stream()
+            AnnotationSupport.findAnnotatedFields(currClass, ExcludeBean.class).stream()
                     .map(Field::getType)
                     .forEach(excludedBeanTypes::add);
 
@@ -94,7 +94,7 @@ class ClassScanning {
                     .map(Method::getReturnType)
                     .forEach(excludedBeanTypes::add);
 
-            findAnnotatedFields(currClass, Inject.class).stream()
+            AnnotationSupport.findAnnotatedFields(currClass, Inject.class).stream()
                     .map(Field::getType)
                     .forEach(cls -> addClassesToProcess(classesToProcess, cls));
 
@@ -107,11 +107,11 @@ class ClassScanning {
                     .flatMap(cons -> getExecutableParameterTypes(cons, explicitInjection).stream())
                     .forEach(cls -> addClassesToProcess(classesToProcess, cls));
 
-            findAnnotatedFields(currClass, Produces.class).stream()
+            findAnnotatedDeclaredFields(currClass, Produces.class).stream()
                     .map(Field::getType)
                     .forEach(cls -> addClassesToProcess(classesToProcess, cls));
 
-            AnnotationSupport.findAnnotatedMethods(currClass, Produces.class, HierarchyTraversalMode.BOTTOM_UP).stream()
+            findAnnotatedDeclaredMethods(currClass, Produces.class).stream()
                     .flatMap(method ->
                         Stream.concat(
                                 getExecutableParameterTypes(method, explicitInjection).stream(),
@@ -288,6 +288,10 @@ class ClassScanning {
         return asList(clazz.getDeclaredFields());
     }
 
+    private static List<Method> getDeclaredMethods(Class<?> clazz) {
+        return asList(clazz.getDeclaredMethods());
+    }
+
     private static boolean isMethodShadowedByLocalFields(Field field, List<Field> localFields) {
         return localFields.stream()
                 .anyMatch((local) -> isFieldShadowedBy(field, local));
@@ -297,12 +301,18 @@ class ClassScanning {
         return upper.getType().equals(lower.getType());
     }
 
-    private static List<Field> findAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotationType) {
+    private static List<Field> findAnnotatedDeclaredFields(Class<?> clazz, Class<? extends Annotation> annotationType) {
         return getDeclaredFields(clazz).stream()
                 .filter((field) -> isAnnotated(field, annotationType))
                 .collect(CollectionUtils.toUnmodifiableList());
     }
 
+    private static List<Method> findAnnotatedDeclaredMethods(Class<?> clazz, Class<? extends Annotation> annotationType) {
+        return getDeclaredMethods(clazz).stream()
+                .filter((field) -> isAnnotated(field, annotationType))
+                .collect(CollectionUtils.toUnmodifiableList());
+    }
+
     private static List<Constructor<?>> getDeclaredConstructors(Class<?> clazz) {
         return asList(clazz.getDeclaredConstructors());
     }


Test patch:
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/InheritanceTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/AnnotationsInheritanceTest.java
similarity index 94%
rename from junit5/src/test/java/org/jboss/weld/junit5/auto/InheritanceTest.java
rename to junit5/src/test/java/org/jboss/weld/junit5/auto/AnnotationsInheritanceTest.java
index c7104ed..abce230 100644
--- a/junit5/src/test/java/org/jboss/weld/junit5/auto/InheritanceTest.java
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/AnnotationsInheritanceTest.java
@@ -1,107 +1,107 @@
-package org.jboss.weld.junit5.auto;
-
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import javax.inject.Inject;
-
-import org.jboss.weld.junit5.auto.beans.Engine;
-import org.jboss.weld.junit5.auto.beans.V8;
-import org.jboss.weld.junit5.auto.extension.AddedExtension;
-import org.jboss.weld.junit5.auto.interceptorAndDecorator.DecoratedBean;
-import org.jboss.weld.junit5.auto.interceptorAndDecorator.InterceptedBean;
-import org.jboss.weld.junit5.auto.interceptorAndDecorator.TestDecorator;
-import org.jboss.weld.junit5.auto.interceptorAndDecorator.TestInterceptor;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-/**
- * Tests the inheritance of Annotations.
- */
-public class InheritanceTest {
-
-    @EnableAutoWeld
-    @AddBeanClasses(V8.class)
-    class BaseAddBeanClassesTest {
-    }
-
-    @Nested
-    class AddBeanClassesTest extends BaseAddBeanClassesTest {
-        @Inject
-        private Engine engine;
-
-        @Test
-        @DisplayName("Test that @AddBeanClasses pulls in V8 to fulfill the injected Engine interface")
-        void test() {
-            assertNotNull(engine);
-        }
-    }
-
-    @EnableAutoWeld
-    @AddEnabledDecorators(TestDecorator.class)
-    class BaseAddDecoratorTest {
-    }
-
-    @Nested
-    class AddDecoratorTest extends BaseAddDecoratorTest {
-        @Inject
-        DecoratedBean bean;
-
-        @Test
-        public void testBeanIsDecorated() {
-            Assertions.assertNotNull(bean);
-            Assertions.assertEquals(TestDecorator.class.toString() + DecoratedBean.class.toString(), bean.ping());
-        }
-    }
-
-    @EnableAutoWeld
-    @AddExtensions(AddedExtension.class)
-    class BaseAddExtensionsTest {
-    }
-
-    @Nested
-    class AddExtensionsTest extends BaseAddExtensionsTest {
-        @Test
-        @DisplayName("Test that @AddExtensions adds the specified extensions")
-        void test() {
-            assertTrue(AddedExtension.isEnabled());
-        }
-    }
-
-    @EnableAutoWeld
-    @AddEnabledInterceptors(TestInterceptor.class)
-    class BaseAddInterceptorTest {
-    }
-
-    @Nested
-    class AddInterceptorTest extends BaseAddInterceptorTest {
-        @Inject
-        InterceptedBean bean;
-
-        @Test
-        public void testBeanIsIntercepted() {
-            Assertions.assertNotNull(bean);
-            Assertions.assertEquals(TestInterceptor.class.toString() + InterceptedBean.class.toString(), bean.ping());
-        }
-    }
-
-    @EnableAutoWeld
-    @AddPackages(Engine.class)
-    class BaseAddPackagesTest {
-    }
-
-    @Nested
-    class AddPackagesTest extends BaseAddPackagesTest {
-        @Inject
-        private V8 engine;
-
-        @Test
-        @DisplayName("Test that @AddPackages pulls in V8 (without bean defining annotation) to fulfill the injected Engine interface")
-        void test() {
-            assertNotNull(engine);
-        }
-    }
-
-}
+package org.jboss.weld.junit5.auto;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import javax.inject.Inject;
+
+import org.jboss.weld.junit5.auto.beans.Engine;
+import org.jboss.weld.junit5.auto.beans.V8;
+import org.jboss.weld.junit5.auto.extension.AddedExtension;
+import org.jboss.weld.junit5.auto.interceptorAndDecorator.DecoratedBean;
+import org.jboss.weld.junit5.auto.interceptorAndDecorator.InterceptedBean;
+import org.jboss.weld.junit5.auto.interceptorAndDecorator.TestDecorator;
+import org.jboss.weld.junit5.auto.interceptorAndDecorator.TestInterceptor;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests the inheritance of "Weld-JUnit" annotations from test parent classes.
+ */
+public class AnnotationsInheritanceTest {
+
+    @EnableAutoWeld
+    @AddBeanClasses(V8.class)
+    class BaseAddBeanClassesTest {
+    }
+
+    @Nested
+    class AddBeanClassesTest extends BaseAddBeanClassesTest {
+        @Inject
+        private Engine engine;
+
+        @Test
+        @DisplayName("Test that @AddBeanClasses pulls in V8 to fulfill the injected Engine interface")
+        void test() {
+            assertNotNull(engine);
+        }
+    }
+
+    @EnableAutoWeld
+    @AddEnabledDecorators(TestDecorator.class)
+    class BaseAddDecoratorTest {
+    }
+
+    @Nested
+    class AddDecoratorTest extends BaseAddDecoratorTest {
+        @Inject
+        DecoratedBean bean;
+
+        @Test
+        public void testBeanIsDecorated() {
+            Assertions.assertNotNull(bean);
+            Assertions.assertEquals(TestDecorator.class.toString() + DecoratedBean.class.toString(), bean.ping());
+        }
+    }
+
+    @EnableAutoWeld
+    @AddExtensions(AddedExtension.class)
+    class BaseAddExtensionsTest {
+    }
+
+    @Nested
+    class AddExtensionsTest extends BaseAddExtensionsTest {
+        @Test
+        @DisplayName("Test that @AddExtensions adds the specified extensions")
+        void test() {
+            assertTrue(AddedExtension.isEnabled());
+        }
+    }
+
+    @EnableAutoWeld
+    @AddEnabledInterceptors(TestInterceptor.class)
+    class BaseAddInterceptorTest {
+    }
+
+    @Nested
+    class AddInterceptorTest extends BaseAddInterceptorTest {
+        @Inject
+        InterceptedBean bean;
+
+        @Test
+        public void testBeanIsIntercepted() {
+            Assertions.assertNotNull(bean);
+            Assertions.assertEquals(TestInterceptor.class.toString() + InterceptedBean.class.toString(), bean.ping());
+        }
+    }
+
+    @EnableAutoWeld
+    @AddPackages(Engine.class)
+    class BaseAddPackagesTest {
+    }
+
+    @Nested
+    class AddPackagesTest extends BaseAddPackagesTest {
+        @Inject
+        private V8 engine;
+
+        @Test
+        @DisplayName("Test that @AddPackages pulls in V8 (without bean defining annotation) to fulfill the injected Engine interface")
+        void test() {
+            assertNotNull(engine);
+        }
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/InheritedInjectedTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/InheritedInjectedTest.java
new file mode 100644
index 0000000..458b31e
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/InheritedInjectedTest.java
@@ -0,0 +1,54 @@
+package org.jboss.weld.junit5.auto;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import javax.enterprise.context.Dependent;
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+@EnableAutoWeld
+public class InheritedInjectedTest {
+
+    @Dependent
+    static class Foo {
+    }
+
+    static class BaseClass {
+        @Inject
+        Foo foo;
+    }
+
+    @Nested
+    class InjectIntoInheritedFieldsFromTestBaseClassTest extends BaseClass {
+
+        @Test
+        @DisplayName("Test injected fields a test class inherits from a base class")
+        void test() {
+            assertNotNull(foo);
+        }
+    }
+
+    @Dependent
+    static class SubClass extends BaseClass {
+    }
+
+    @Nested
+    class InjectIntoInheritedFieldsFromBeanBaseClassTest {
+
+        @Inject
+        SubClass inheritsInjected;
+
+        /**
+         * Injection of {@link #inheritsInjected} requires {@link InheritedInjectedTest.Foo} having been identified and added as a bean class to Weld.
+         */
+        @Test
+        @DisplayName("Test inherited injected fields")
+        void test() {
+            assertNotNull(inheritsInjected.foo);
+        }
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/InheritedProducerFieldTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/InheritedProducerFieldTest.java
new file mode 100644
index 0000000..687519e
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/InheritedProducerFieldTest.java
@@ -0,0 +1,48 @@
+package org.jboss.weld.junit5.auto;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import javax.enterprise.context.Dependent;
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+@EnableAutoWeld
+public class InheritedProducerFieldTest {
+
+    @Dependent
+    static class Foo {
+    }
+
+    static class BaseClass {
+        @Produces
+        Foo baseFooProducer = new Foo();
+    }
+
+    @Nested
+    class DontAddBeanClassesInheritedFromTestBaseClassTest extends BaseClass {
+        @Test
+        void test(BeanManager beanManager) {
+            assertEquals(0, beanManager.getBeans(Foo.class).size());
+        }
+    }
+
+    @Dependent
+    static class SubClass extends BaseClass {
+    }
+
+    @Nested
+    class DontAddBeanClassesInheritedFromBeanBaseClassTest {
+        @Inject
+        SubClass subClass;
+
+        @Test
+        void test(BeanManager beanManager) {
+            assertEquals(0, beanManager.getBeans(Foo.class).size());
+        }
+    }
+
+}
diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/InheritedProducerMethodTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/InheritedProducerMethodTest.java
new file mode 100644
index 0000000..43641af
--- /dev/null
+++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/InheritedProducerMethodTest.java
@@ -0,0 +1,50 @@
+package org.jboss.weld.junit5.auto;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import javax.enterprise.context.Dependent;
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+@EnableAutoWeld
+public class InheritedProducerMethodTest {
+
+    @Dependent
+    static class Foo {
+    }
+
+    static class BaseClass {
+        @Produces
+        Foo baseFooProducer() {
+            return new Foo();
+        }
+    }
+
+    @Nested
+    class DontAddBeanClassesInheritedFromTestBaseClassTest extends BaseClass {
+        @Test
+        void test(BeanManager beanManager) {
+            assertEquals(0, beanManager.getBeans(Foo.class).size());
+        }
+    }
+
+    @Dependent
+    static class SubClass extends BaseClass {
+    }
+
+    @Nested
+    class DontAddBeanClassesInheritedFromBeanBaseClassTest {
+        @Inject
+        SubClass subClass;
+
+        @Test
+        void test(BeanManager beanManager) {
+            assertEquals(0, beanManager.getBeans(Foo.class).size());
+        }
+    }
+
+}