diff --git a/junit5/src/main/java/org/jboss/weld/junit5/ExplicitParamInjection.java b/junit5/src/main/java/org/jboss/weld/junit5/ExplicitParamInjection.java index 18c1905d..82fd66dd 100644 --- a/junit5/src/main/java/org/jboss/weld/junit5/ExplicitParamInjection.java +++ b/junit5/src/main/java/org/jboss/weld/junit5/ExplicitParamInjection.java @@ -22,16 +22,26 @@ import jakarta.enterprise.inject.Default; /** - * An annotation used to enforce explicit parameter annotation. When applied, Weld will only attempt to resolve method - * parameters which have qualifiers. In case no qualifier is required for your bean, add the {@link Default} qualifier, see CDI - * specification for in depth explanation on qualifiers. + * An annotation used to enforce explicit parameter annotation. When applied and set to {@code true}, Weld will only attempt to + * resolve method parameters which have qualifiers. In case no qualifier is required for your bean, add the {@link Default} + * qualifier, see CDI specification for in depth explanation on qualifiers. * - * This annotation can be applied either on test class, in which case it affects parameter injection in all methods, or on - * a method. + * This annotation can be applied either on a test class, in which case it affects parameter injection in all methods, or on + * a test method. + * + * Nested classes inherit the behavior declared by their enclosing class but can re-declare this annotation along with the + * {@link #apply()} parameter to override the behavior. * * @author Matej Novotny */ @Retention(RetentionPolicy.RUNTIME) public @interface ExplicitParamInjection { + /** + * If set to {@code true}, Weld will only attempt to resolve parameters which have CDI qualifier annotations. + * + * @return {@code true} by default; can be explicitly set to {@code false} to make Weld attempt to resolve all parameters + */ + boolean apply() default true; + } 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 6c0b98c7..54887c15 100644 --- a/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java +++ b/junit5/src/main/java/org/jboss/weld/junit5/WeldJunit5Extension.java @@ -103,13 +103,22 @@ private static void storeExplicitParamResolutionInformation(ExtensionContext ec) return; } // check class-level annotation - for (Annotation annotation : ec.getRequiredTestClass().getAnnotations()) { - if (annotation.annotationType().equals(ExplicitParamInjection.class)) { - setExplicitInjectionInfoToStore(ec, true); - break; + Class inspectedTestClass = ec.getRequiredTestClass(); + ExplicitParamInjection explicitParamInjection = inspectedTestClass.getAnnotation(ExplicitParamInjection.class); + if (explicitParamInjection != null) { + setExplicitInjectionInfoToStore(ec, explicitParamInjection.apply()); + } else { + // if not found, it can still be a nested class + // inspect enclosing classes until first annotation is found or until we hit top-level class + inspectedTestClass = inspectedTestClass.getEnclosingClass(); + while (inspectedTestClass != null && explicitParamInjection == null) { + explicitParamInjection = inspectedTestClass.getAnnotation(ExplicitParamInjection.class); + if (explicitParamInjection != null) { + setExplicitInjectionInfoToStore(ec, explicitParamInjection.apply()); + } + inspectedTestClass = inspectedTestClass.getEnclosingClass(); } } - } @Override diff --git a/junit5/src/test/java/org/jboss/weld/junit5/auto/ParametersAutoConfigTest.java b/junit5/src/test/java/org/jboss/weld/junit5/auto/ParametersAutoConfigTest.java index ee766e5a..316868ac 100644 --- a/junit5/src/test/java/org/jboss/weld/junit5/auto/ParametersAutoConfigTest.java +++ b/junit5/src/test/java/org/jboss/weld/junit5/auto/ParametersAutoConfigTest.java @@ -2,7 +2,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import org.jboss.weld.junit5.basic.Foo; import org.jboss.weld.junit5.explicitInjection.Bar; @@ -16,7 +15,7 @@ public class ParametersAutoConfigTest { @DisplayName("Ensure the parameters Foo and Bar are automatically included in container with no configuration") void test(Foo foo, Bar bar) { assertNotNull(bar); - assertNull(bar.ping()); + assertEquals(Bar.class.getSimpleName(), bar.ping()); assertNotNull(foo); assertEquals(foo.getBar(), "baz"); } diff --git a/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/Bar.java b/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/Bar.java index fed92ae4..0225e98e 100644 --- a/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/Bar.java +++ b/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/Bar.java @@ -28,6 +28,7 @@ public class Bar { private String someText = null; public Bar() { + this.someText = Bar.class.getSimpleName(); } public Bar(String someText) { diff --git a/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/CustomExtension.java b/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/CustomExtension.java index d497b7f3..e0477030 100644 --- a/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/CustomExtension.java +++ b/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/CustomExtension.java @@ -31,8 +31,11 @@ public class CustomExtension implements ParameterResolver { @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { - // dumb approach but we only ever resolve Bar anyway :) - return new Bar(CustomExtension.class.getSimpleName()); + if (parameterContext.getParameter().getType().equals(Bar.class)) { + return new Bar(CustomExtension.class.getSimpleName()); + } else { + throw new IllegalStateException(getClass().getName() + " can only resolve parameter Bar!"); + } } @Override diff --git a/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/ExplicitParameterInjectionNestedClass2Test.java b/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/ExplicitParameterInjectionNestedClass2Test.java new file mode 100644 index 00000000..f01784db --- /dev/null +++ b/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/ExplicitParameterInjectionNestedClass2Test.java @@ -0,0 +1,99 @@ +package org.jboss.weld.junit5.explicitInjection; + +import jakarta.enterprise.inject.Default; + +import org.jboss.weld.junit5.ExplicitParamInjection; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +// tests that explicit param injection can work on a class level but not propagate to nested test +@ExtendWith(WeldJunit5Extension.class) +@ExplicitParamInjection(apply = false) +public class ExplicitParameterInjectionNestedClass2Test { + + @Test + public void testParameterResolution(@Default Foo foo, Bar bar, @MyQualifier BeanWithQualifier bean) { + // Bar should be resolved by another extension + Assertions.assertNotNull(bar); + Assertions.assertEquals(Bar.class.getSimpleName(), bar.ping()); + // Foo should be resolved as usual + Assertions.assertNotNull(foo); + Assertions.assertEquals(Foo.class.getSimpleName(), foo.ping()); + // BeanWithQualifier should be resolved + Assertions.assertNotNull(bean); + Assertions.assertEquals(BeanWithQualifier.class.getSimpleName(), bean.ping()); + } + + @Nested + class NestedTestClass { + + @Test + public void testParameterResolution(@Default Foo foo, Bar bar, @MyQualifier BeanWithQualifier bean) { + // Weld will now claim all parameters as beans, even Bar + Assertions.assertNotNull(bar); + Assertions.assertEquals(Bar.class.getSimpleName(), bar.ping()); + // Foo should be resolved as usual + Assertions.assertNotNull(foo); + Assertions.assertEquals(Foo.class.getSimpleName(), foo.ping()); + // BeanWithQualifier should be resolved + Assertions.assertNotNull(bean); + Assertions.assertEquals(BeanWithQualifier.class.getSimpleName(), bean.ping()); + } + + @Nested + class TwiceNestedTestClass1 { + + @Test + public void testParameterResolution(@Default Foo foo, Bar bar, @MyQualifier BeanWithQualifier bean) { + // Bar should be resolved by another extension + Assertions.assertNotNull(bar); + Assertions.assertEquals(Bar.class.getSimpleName(), bar.ping()); + // Foo should be resolved as usual + Assertions.assertNotNull(foo); + Assertions.assertEquals(Foo.class.getSimpleName(), foo.ping()); + // BeanWithQualifier should be resolved + Assertions.assertNotNull(bean); + Assertions.assertEquals(BeanWithQualifier.class.getSimpleName(), bean.ping()); + } + } + + @Nested + @ExplicitParamInjection(apply = true) + @ExtendWith(CustomExtension.class) // TwiceNestedTestClass2 and ThriceNestedClass will both use this + class TwiceNestedTestClass2 { + + @Test + public void testParameterResolution(@Default Foo foo, Bar bar, @MyQualifier BeanWithQualifier bean) { + // Bar should be resolved by another extension + Assertions.assertNotNull(bar); + Assertions.assertEquals(CustomExtension.class.getSimpleName(), bar.ping()); + // Foo should be resolved as usual + Assertions.assertNotNull(foo); + Assertions.assertEquals(Foo.class.getSimpleName(), foo.ping()); + // BeanWithQualifier should be resolved + Assertions.assertNotNull(bean); + Assertions.assertEquals(BeanWithQualifier.class.getSimpleName(), bean.ping()); + } + + @Nested + class ThriceNestedClass { + + @Test + public void testParameterResolution(@Default Foo foo, Bar bar, @MyQualifier BeanWithQualifier bean) { + // Bar should be resolved by another extension + Assertions.assertNotNull(bar); + Assertions.assertEquals(CustomExtension.class.getSimpleName(), bar.ping()); + // Foo should be resolved as usual + Assertions.assertNotNull(foo); + Assertions.assertEquals(Foo.class.getSimpleName(), foo.ping()); + // BeanWithQualifier should be resolved + Assertions.assertNotNull(bean); + Assertions.assertEquals(BeanWithQualifier.class.getSimpleName(), bean.ping()); + } + } + } + } +} diff --git a/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/ExplicitParameterInjectionNestedClassTest.java b/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/ExplicitParameterInjectionNestedClassTest.java new file mode 100644 index 00000000..791a4458 --- /dev/null +++ b/junit5/src/test/java/org/jboss/weld/junit5/explicitInjection/ExplicitParameterInjectionNestedClassTest.java @@ -0,0 +1,102 @@ +package org.jboss.weld.junit5.explicitInjection; + +import jakarta.enterprise.inject.Default; + +import org.jboss.weld.junit5.ExplicitParamInjection; +import org.jboss.weld.junit5.WeldJunit5Extension; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +// note that @ExtendWith(CustomExtension.class) has to be on each method separately +// the inheritance of this would otherwise cause failures for ThriceNestedClass where Weld claims all parameters +@ExtendWith(WeldJunit5Extension.class) +@ExplicitParamInjection(apply = true) +public class ExplicitParameterInjectionNestedClassTest { + + @Test + @ExtendWith(CustomExtension.class) + public void testParameterResolution(@Default Foo foo, Bar bar, @MyQualifier BeanWithQualifier bean) { + // Bar should be resolved by another extension + Assertions.assertNotNull(bar); + Assertions.assertEquals(CustomExtension.class.getSimpleName(), bar.ping()); + // Foo should be resolved as usual + Assertions.assertNotNull(foo); + Assertions.assertEquals(Foo.class.getSimpleName(), foo.ping()); + // BeanWithQualifier should be resolved + Assertions.assertNotNull(bean); + Assertions.assertEquals(BeanWithQualifier.class.getSimpleName(), bean.ping()); + } + + @Nested + class NestedTestClass { + + @Test + @ExtendWith(CustomExtension.class) + public void testParameterResolution(@Default Foo foo, Bar bar, @MyQualifier BeanWithQualifier bean) { + // Bar should be resolved by another extension + Assertions.assertNotNull(bar); + Assertions.assertEquals(CustomExtension.class.getSimpleName(), bar.ping()); + // Foo should be resolved as usual + Assertions.assertNotNull(foo); + Assertions.assertEquals(Foo.class.getSimpleName(), foo.ping()); + // BeanWithQualifier should be resolved + Assertions.assertNotNull(bean); + Assertions.assertEquals(BeanWithQualifier.class.getSimpleName(), bean.ping()); + } + + @Nested + class TwiceNestedTestClass1 { + + @Test + @ExtendWith(CustomExtension.class) + public void testParameterResolution(@Default Foo foo, Bar bar, @MyQualifier BeanWithQualifier bean) { + // Bar should be resolved by another extension + Assertions.assertNotNull(bar); + Assertions.assertEquals(CustomExtension.class.getSimpleName(), bar.ping()); + // Foo should be resolved as usual + Assertions.assertNotNull(foo); + Assertions.assertEquals(Foo.class.getSimpleName(), foo.ping()); + // BeanWithQualifier should be resolved + Assertions.assertNotNull(bean); + Assertions.assertEquals(BeanWithQualifier.class.getSimpleName(), bean.ping()); + } + } + + @Nested + @ExplicitParamInjection(apply = false) + class TwiceNestedTestClass2 { + + @Test + public void testParameterResolution(@Default Foo foo, Bar bar, @MyQualifier BeanWithQualifier bean) { + // Bar should be resolved by another extension + Assertions.assertNotNull(bar); + Assertions.assertEquals(Bar.class.getSimpleName(), bar.ping()); + // Foo should be resolved as usual + Assertions.assertNotNull(foo); + Assertions.assertEquals(Foo.class.getSimpleName(), foo.ping()); + // BeanWithQualifier should be resolved + Assertions.assertNotNull(bean); + Assertions.assertEquals(BeanWithQualifier.class.getSimpleName(), bean.ping()); + } + + @Nested + class ThriceNestedClass { + + @Test + public void testParameterResolution(@Default Foo foo, Bar bar, @MyQualifier BeanWithQualifier bean) { + // NOTE: Bar should be Weld again! + Assertions.assertNotNull(bar); + Assertions.assertEquals(Bar.class.getSimpleName(), bar.ping()); + // Foo should be resolved as usual + Assertions.assertNotNull(foo); + Assertions.assertEquals(Foo.class.getSimpleName(), foo.ping()); + // BeanWithQualifier should be resolved + Assertions.assertNotNull(bean); + Assertions.assertEquals(BeanWithQualifier.class.getSimpleName(), bean.ping()); + } + } + } + } +}