Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QuarkusComponentTest: support parameterized test methods #38929

Merged
merged 2 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/src/main/asciidoc/getting-started-testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1666,11 +1666,14 @@
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`.

Check warning on line 1672 in docs/src/main/asciidoc/getting-started-testing.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using ', which (non restrictive clause preceded by a comma)' or 'that (restrictive clause without a comma)' rather than 'which'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using ', which (non restrictive clause preceded by a comma)' or 'that (restrictive clause without a comma)' rather than 'which'.", "location": {"path": "docs/src/main/asciidoc/getting-started-testing.adoc", "range": {"start": {"line": 1672, "column": 17}}}, "severity": "INFO"}
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`.

Check warning on line 1675 in docs/src/main/asciidoc/getting-started-testing.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Headings] Use sentence-style capitalization in 'Auto Mocking Unsatisfied Dependencies'. Raw Output: {"message": "[Quarkus.Headings] Use sentence-style capitalization in 'Auto Mocking Unsatisfied Dependencies'.", "location": {"path": "docs/src/main/asciidoc/getting-started-testing.adoc", "range": {"start": {"line": 1675, "column": 202}}}, "severity": "INFO"}

=== Auto Mocking Unsatisfied Dependencies

Unlike in regular CDI environments the test does not fail if a component injects an unsatisfied dependency.
Expand Down
5 changes: 5 additions & 0 deletions test-framework/junit5-component/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@
<artifactId>jakarta.ws.rs-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -999,7 +1002,7 @@ private List<Field> findInjectFields(Class<?> testClass) {
}

private List<Parameter> findInjectParams(Class<?> testClass) {
List<Method> testMethods = findMethods(testClass, m -> m.isAnnotationPresent(Test.class));
List<Method> testMethods = findMethods(testClass, QuarkusComponentTestExtension::isTestMethod);
List<Parameter> ret = new ArrayList<>();
for (Method method : testMethods) {
for (Parameter param : method.getParameters()) {
Expand All @@ -1015,7 +1018,7 @@ private List<Parameter> findInjectParams(Class<?> testClass) {
}

private List<Parameter> findInjectMockParams(Class<?> testClass) {
List<Method> testMethods = findMethods(testClass, m -> m.isAnnotationPresent(Test.class));
List<Method> testMethods = findMethods(testClass, QuarkusComponentTestExtension::isTestMethod);
List<Parameter> ret = new ArrayList<>();
for (Method method : testMethods) {
for (Parameter param : method.getParameters()) {
Expand All @@ -1028,6 +1031,12 @@ private List<Parameter> 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<Field> findFields(Class<?> testClass, List<Class<? extends Annotation>> annotations) {
List<Field> fields = new ArrayList<>();
Class<?> current = testClass;
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}

}
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Loading