diff --git a/docs/src/main/asciidoc/getting-started-testing.adoc b/docs/src/main/asciidoc/getting-started-testing.adoc
index 63ef64af6eeb1..9d62dc16b4d58 100644
--- a/docs/src/main/asciidoc/getting-started-testing.adoc
+++ b/docs/src/main/asciidoc/getting-started-testing.adoc
@@ -1666,11 +1666,14 @@ The fields annotated with `@Inject` and `@InjectMock` are injected after a test
Finally, the CDI request context is activated and terminated per each test method.
=== Injection
+
Test class fields annotated with `@jakarta.inject.Inject` and `@io.quarkus.test.InjectMock` are injected after a test instance is created.
Dependent beans injected into these fields are correctly destroyed before a test instance is destroyed.
Parameters of a test method for which a matching bean exists are resolved unless annotated with `@io.quarkus.test.component.SkipInject`.
Dependent beans injected into the test method arguments are correctly destroyed after the test method completes.
+NOTE: Arguments of a `@ParameterizedTest` method that are provided by an `ArgumentsProvider`, for example with `@org.junit.jupiter.params.provider.ValueArgumentsProvider`, must be annotated with `@SkipInject`.
+
=== Auto Mocking Unsatisfied Dependencies
Unlike in regular CDI environments the test does not fail if a component injects an unsatisfied dependency.
diff --git a/test-framework/junit5-component/pom.xml b/test-framework/junit5-component/pom.xml
index 8d6bb92298438..156a0af5256ae 100644
--- a/test-framework/junit5-component/pom.xml
+++ b/test-framework/junit5-component/pom.xml
@@ -74,6 +74,11 @@
jakarta.ws.rs-api
test
+
+ org.assertj
+ assertj-core
+ test
+
diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java
index 4a8bd75c25f80..8279bbf99c84b 100644
--- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java
+++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestConfiguration.java
@@ -18,7 +18,6 @@
import jakarta.inject.Provider;
import org.jboss.logging.Logger;
-import org.junit.jupiter.api.Test;
import io.quarkus.arc.InjectableInstance;
import io.quarkus.arc.processor.AnnotationsTransformer;
@@ -98,7 +97,7 @@ QuarkusComponentTestConfiguration update(Class> testClass) {
// - not annotated with @InjectMock
// - not annotated with @SkipInject
for (Method method : current.getDeclaredMethods()) {
- if (method.isAnnotationPresent(Test.class)) {
+ if (QuarkusComponentTestExtension.isTestMethod(method)) {
for (Parameter param : method.getParameters()) {
if (QuarkusComponentTestExtension.BUILTIN_PARAMETER.test(param)
|| param.isAnnotationPresent(InjectMock.class)
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 8c1873054e2c3..0046c09eee626 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
@@ -5,6 +5,7 @@
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -63,6 +64,7 @@
import org.jboss.jandex.Type;
import org.jboss.jandex.Type.Kind;
import org.jboss.logging.Logger;
+import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
@@ -79,6 +81,7 @@
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
import io.quarkus.arc.All;
import io.quarkus.arc.Arc;
@@ -269,7 +272,7 @@ public boolean supportsParameter(ParameterContext parameterContext, ExtensionCon
// Target is empty for constructor or static method
parameterContext.getTarget().isPresent()
// Only test methods are supported
- && parameterContext.getDeclaringExecutable().isAnnotationPresent(Test.class)
+ && isTestMethod(parameterContext.getDeclaringExecutable())
// A method/param annotated with @SkipInject is never supported
&& !parameterContext.isAnnotated(SkipInject.class)
&& !parameterContext.getDeclaringExecutable().isAnnotationPresent(SkipInject.class)
@@ -999,7 +1002,7 @@ private List findInjectFields(Class> testClass) {
}
private List findInjectParams(Class> testClass) {
- List testMethods = findMethods(testClass, m -> m.isAnnotationPresent(Test.class));
+ List testMethods = findMethods(testClass, QuarkusComponentTestExtension::isTestMethod);
List ret = new ArrayList<>();
for (Method method : testMethods) {
for (Parameter param : method.getParameters()) {
@@ -1015,7 +1018,7 @@ private List findInjectParams(Class> testClass) {
}
private List findInjectMockParams(Class> testClass) {
- List testMethods = findMethods(testClass, m -> m.isAnnotationPresent(Test.class));
+ List testMethods = findMethods(testClass, QuarkusComponentTestExtension::isTestMethod);
List ret = new ArrayList<>();
for (Method method : testMethods) {
for (Parameter param : method.getParameters()) {
@@ -1028,6 +1031,12 @@ private List findInjectMockParams(Class> testClass) {
return ret;
}
+ static boolean isTestMethod(Executable method) {
+ return method.isAnnotationPresent(Test.class)
+ || method.isAnnotationPresent(ParameterizedTest.class)
+ || method.isAnnotationPresent(RepeatedTest.class);
+ }
+
private List findFields(Class> testClass, List> annotations) {
List fields = new ArrayList<>();
Class> current = testClass;
diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/paraminject/ParameterInjectionParameterizedTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/paraminject/ParameterInjectionParameterizedTest.java
new file mode 100644
index 0000000000000..3a3f9f2fa8891
--- /dev/null
+++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/paraminject/ParameterInjectionParameterizedTest.java
@@ -0,0 +1,47 @@
+package io.quarkus.test.component.paraminject;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jakarta.annotation.PreDestroy;
+import jakarta.enterprise.context.Dependent;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import io.quarkus.test.component.QuarkusComponentTest;
+import io.quarkus.test.component.SkipInject;
+
+@QuarkusComponentTest
+public class ParameterInjectionParameterizedTest {
+
+ @ParameterizedTest
+ @ValueSource(strings = { "alpha", "bravo", "delta" })
+ public void testParamsInjection(@SkipInject String param, Converter converter) {
+ Assertions.assertThat(converter.convert(param)).isIn("ALPHA", "BRAVO", "DELTA");
+ }
+
+ @AfterAll
+ public static void afterAll() {
+ assertEquals(3, Converter.DESTROYED.get());
+ }
+
+ @Dependent
+ public static class Converter {
+
+ static final AtomicInteger DESTROYED = new AtomicInteger();
+
+ String convert(String param) {
+ return param.toUpperCase();
+ }
+
+ @PreDestroy
+ void destroy() {
+ DESTROYED.incrementAndGet();
+ }
+ }
+
+}
diff --git a/test-framework/junit5-component/src/test/java/io/quarkus/test/component/paraminject/ParameterInjectionRepeatedTest.java b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/paraminject/ParameterInjectionRepeatedTest.java
new file mode 100644
index 0000000000000..1d4504c4ca798
--- /dev/null
+++ b/test-framework/junit5-component/src/test/java/io/quarkus/test/component/paraminject/ParameterInjectionRepeatedTest.java
@@ -0,0 +1,46 @@
+package io.quarkus.test.component.paraminject;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jakarta.annotation.PreDestroy;
+import jakarta.enterprise.context.Dependent;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.RepetitionInfo;
+
+import io.quarkus.test.component.QuarkusComponentTest;
+
+@QuarkusComponentTest
+public class ParameterInjectionRepeatedTest {
+ @RepeatedTest(10)
+ public void testParamsInjection(
+ // component under test
+ Converter converter,
+ // ignored automatically, no need for `@SkipInject`
+ RepetitionInfo info) {
+ assertEquals(10, info.getTotalRepetitions());
+ assertEquals("FOOBAR", converter.convert("fooBar"));
+ }
+
+ @AfterAll
+ public static void afterAll() {
+ assertEquals(10, Converter.DESTROYED.get());
+ }
+
+ @Dependent
+ public static class Converter {
+ static final AtomicInteger DESTROYED = new AtomicInteger();
+
+ String convert(String param) {
+ return param.toUpperCase();
+ }
+
+ @PreDestroy
+ void destroy() {
+ DESTROYED.incrementAndGet();
+ }
+ }
+}