+ * The initial set includes the {@link JaxrsSingletonTransformer}.
+ *
+ * @see AnnotationsTransformer
+ * @see QuarkusComponentTestExtension#addAnnotationsTransformer(AnnotationsTransformer)
+ */
+ Class extends AnnotationsTransformer>[] annotationsTransformers() default {};
+
}
diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java
index 00960605b882a..3aa420659512b 100644
--- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java
+++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java
@@ -171,12 +171,14 @@ public class QuarkusComponentTestExtension
private final AtomicBoolean useDefaultConfigProperties = new AtomicBoolean();
private final AtomicBoolean addNestedClassesAsComponents = new AtomicBoolean(true);
private final AtomicInteger configSourceOrdinal = new AtomicInteger(DEFAULT_CONFIG_SOURCE_ORDINAL);
+ private final List
* For primitives the default values as defined in the JLS are used. For any other type {@code null} is injected.
*
- * @return the extension
+ * @return self
*/
public QuarkusComponentTestExtension useDefaultConfigProperties() {
this.useDefaultConfigProperties.set(true);
@@ -235,7 +238,7 @@ public QuarkusComponentTestExtension useDefaultConfigProperties() {
* By default, all static nested classes declared on the test class are added to the set of additional components under
* test.
*
- * @return the extension
+ * @return self
*/
public QuarkusComponentTestExtension ignoreNestedClasses() {
this.addNestedClassesAsComponents.set(false);
@@ -247,13 +250,24 @@ public QuarkusComponentTestExtension ignoreNestedClasses() {
* {@value #DEFAULT_CONFIG_SOURCE_ORDINAL} is used.
*
* @param val
- * @return the extension
+ * @return self
*/
public QuarkusComponentTestExtension setConfigSourceOrdinal(int val) {
this.configSourceOrdinal.set(val);
return this;
}
+ /**
+ * Add an additional {@link AnnotationsTransformer}.
+ *
+ * @param transformer
+ * @return self
+ */
+ public QuarkusComponentTestExtension addAnnotationsTransformer(AnnotationsTransformer transformer) {
+ this.additionalAnnotationsTransformers.add(transformer);
+ return this;
+ }
+
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
long start = System.nanoTime();
@@ -295,6 +309,16 @@ public void beforeAll(ExtensionContext context) throws Exception {
}
this.addNestedClassesAsComponents.set(testAnnotation.addNestedClassesAsComponents());
this.configSourceOrdinal.set(testAnnotation.configSourceOrdinal());
+ Class extends AnnotationsTransformer>[] transformers = testAnnotation.annotationsTransformers();
+ if (transformers.length > 0) {
+ for (Class extends AnnotationsTransformer> transformerClass : transformers) {
+ try {
+ this.additionalAnnotationsTransformers.add(transformerClass.getDeclaredConstructor().newInstance());
+ } catch (Exception e) {
+ LOG.errorf("Unable to instantiate %s", transformerClass);
+ }
+ }
+ }
}
// All fields annotated with @Inject represent component classes
Class> current = testClass;
@@ -607,6 +631,11 @@ public void writeResource(Resource resource) throws IOException {
builder.addAnnotationTransformer(AnnotationsTransformer.appliedToField().whenContainsAny(qualifiers)
.whenContainsNone(DotName.createSimple(Inject.class)).thenTransform(t -> t.add(Inject.class)));
+ builder.addAnnotationTransformer(new JaxrsSingletonTransformer());
+ for (AnnotationsTransformer transformer : additionalAnnotationsTransformers) {
+ builder.addAnnotationTransformer(transformer);
+ }
+
// Register:
// 1) Dummy mock beans for all unsatisfied injection points
// 2) Synthetic beans for Config and @ConfigProperty injection points
diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/AnnotationsTransformerTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/AnnotationsTransformerTest.java
new file mode 100644
index 0000000000000..54f8d591db162
--- /dev/null
+++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/AnnotationsTransformerTest.java
@@ -0,0 +1,50 @@
+package io.quarkus.test.component;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+
+import org.jboss.jandex.DotName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.mockito.Mockito;
+
+import io.quarkus.arc.processor.AnnotationsTransformer;
+import io.quarkus.test.InjectMock;
+import io.quarkus.test.component.beans.Charlie;
+
+public class AnnotationsTransformerTest {
+
+ @RegisterExtension
+ static final QuarkusComponentTestExtension extension = new QuarkusComponentTestExtension()
+ .addAnnotationsTransformer(AnnotationsTransformer.appliedToClass()
+ .whenClass(c -> c.declaredAnnotations().isEmpty()
+ && c.annotationsMap().containsKey(DotName.createSimple(Inject.class)))
+ .thenTransform(t -> t.add(Singleton.class)));
+
+ @Inject
+ NotABean bean;
+
+ @InjectMock
+ Charlie charlie;
+
+ @Test
+ public void testPing() {
+ Mockito.when(charlie.ping()).thenReturn("foo");
+ assertEquals("foo", bean.ping());
+ }
+
+ // @Singleton should be added automatically
+ public static class NotABean {
+
+ @Inject
+ Charlie charlie;
+
+ public String ping() {
+ return charlie.ping();
+ }
+
+ }
+
+}
diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/declarative/AnnotationsTransformerTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/declarative/AnnotationsTransformerTest.java
new file mode 100644
index 0000000000000..2c2cfaa90a4d2
--- /dev/null
+++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/declarative/AnnotationsTransformerTest.java
@@ -0,0 +1,65 @@
+package io.quarkus.test.component.declarative;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import jakarta.inject.Inject;
+import jakarta.inject.Singleton;
+
+import org.jboss.jandex.AnnotationTarget.Kind;
+import org.jboss.jandex.ClassInfo;
+import org.jboss.jandex.DotName;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import io.quarkus.arc.processor.AnnotationsTransformer;
+import io.quarkus.test.InjectMock;
+import io.quarkus.test.component.QuarkusComponentTest;
+import io.quarkus.test.component.beans.Charlie;
+import io.quarkus.test.component.declarative.AnnotationsTransformerTest.MyAnnotationsTransformer;
+
+@QuarkusComponentTest(annotationsTransformers = MyAnnotationsTransformer.class)
+public class AnnotationsTransformerTest {
+
+ @Inject
+ NotABean bean;
+
+ @InjectMock
+ Charlie charlie;
+
+ @Test
+ public void testPing() {
+ Mockito.when(charlie.ping()).thenReturn("foo");
+ assertEquals("foo", bean.ping());
+ }
+
+ // @Singleton should be added automatically
+ public static class NotABean {
+
+ @Inject
+ Charlie charlie;
+
+ public String ping() {
+ return charlie.ping();
+ }
+
+ }
+
+ public static class MyAnnotationsTransformer implements AnnotationsTransformer {
+
+ @Override
+ public boolean appliesTo(Kind kind) {
+ return Kind.CLASS == kind;
+ }
+
+ @Override
+ public void transform(TransformationContext context) {
+ ClassInfo c = context.getTarget().asClass();
+ if (c.declaredAnnotations().isEmpty()
+ && c.annotationsMap().containsKey(DotName.createSimple(Inject.class))) {
+ context.transform().add(Singleton.class).done();
+ }
+ }
+
+ }
+
+}
diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/declarative/JaxrsSingletonTransformerTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/declarative/JaxrsSingletonTransformerTest.java
new file mode 100644
index 0000000000000..7d487178b190d
--- /dev/null
+++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/declarative/JaxrsSingletonTransformerTest.java
@@ -0,0 +1,45 @@
+package io.quarkus.test.component.declarative;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import io.quarkus.test.InjectMock;
+import io.quarkus.test.component.QuarkusComponentTest;
+import io.quarkus.test.component.beans.Charlie;
+
+@QuarkusComponentTest
+public class JaxrsSingletonTransformerTest {
+
+ @Inject
+ MyResource resource;
+
+ @InjectMock
+ Charlie charlie;
+
+ @Test
+ public void testPing() {
+ Mockito.when(charlie.ping()).thenReturn("foo");
+ assertEquals("foo", resource.ping());
+ }
+
+ // @Singleton should be added automatically
+ @Path("my")
+ public static class MyResource {
+
+ @Inject
+ Charlie charlie;
+
+ @GET
+ public String ping() {
+ return charlie.ping();
+ }
+
+ }
+
+}