Repository: micronaut-projects/micronaut-test (java)

Issue:
Setting `resolveParameters = false` breaks constructor injection (junit5)
### Expected Behavior

As I understand, setting `resolveParameters = false` should only act on methods.

### Actual Behaviour

It also acts on constructors.

### Example Application

See modified test: https://github.com/micronaut-projects/micronaut-test/compare/master...rorueda:micronaut-test:resolve-parameters-fix

### Version

4.x

Pull request:
fix: do not disable bean resolution for constructors parameters when resolveParameters is set to false
Fixes #898 and adds section about method parameters injection to the docs.

Patch:
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index ffed34dc..13a4a29a 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -52,7 +52,7 @@ jobs:
           github-token: ${{ secrets.GITHUB_TOKEN }}
 
       - name: "🔧 Setup Gradle"
-        uses: gradle/gradle-build-action@v2
+        uses: gradle/gradle-build-action@v2.11.1
 
       - name: "❓ Optional setup step"
         run: |
@@ -77,8 +77,8 @@ jobs:
           check_retries: 'true'
 
       - name: "📜 Upload binary compatibility check results"
-        if: always()
-        uses: actions/upload-artifact@v3
+        if: matrix.java == '17'
+        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
         with:
           name: binary-compatibility-reports
           path: "**/build/reports/binary-compatibility-*.html"
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bcb69667..9aa496e2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -66,13 +66,13 @@ jobs:
           # Store the hash in a file, which is uploaded as a workflow artifact.
           sha256sum $ARTIFACTS | base64 -w0 > artifacts-sha256
       - name: Upload build artifacts
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
         with:
           name: gradle-build-outputs
           path: build/repo/${{ steps.publish.outputs.group }}/*/${{ steps.publish.outputs.version }}/*
           retention-days: 5
       - name: Upload artifacts-sha256
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
         with:
           name: artifacts-sha256
           path: artifacts-sha256
@@ -115,7 +115,7 @@ jobs:
       artifacts-sha256: ${{ steps.set-hash.outputs.artifacts-sha256 }}
     steps:
       - name: Download artifacts-sha256
-        uses: actions/download-artifact@v3
+        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
         with:
           name: artifacts-sha256
       # The SLSA provenance generator expects the hash digest of artifacts to be passed as a job
@@ -148,6 +148,8 @@ jobs:
       - name: Checkout repository
         uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
       - name: Download artifacts
+        # Important: update actions/download-artifact to v4 only when generator_generic_slsa3.yml is also compatible.
+        # See https://github.com/slsa-framework/slsa-github-generator/issues/3068
         uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
         with:
           name: gradle-build-outputs
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 3a1692c5..b730e14d 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,33 +1,33 @@
 [versions]
-micronaut = "4.2.0"
-micronaut-platform = "4.2.0"
+micronaut = "4.2.3"
+micronaut-platform = "4.2.3"
 micronaut-docs = "2.0.0"
 groovy = "4.0.15"
 
-managed-assertj = "3.24.2"
+managed-assertj = "3.25.1"
 managed-hamcrest = "2.2"
-managed-mockito = "5.7.0"
-managed-mockk = "1.13.8"
+managed-mockito = "5.9.0"
+managed-mockk = "1.13.9"
 managed-junit = "5.10.1"
-managed-rest-assured = "5.3.2"
+managed-rest-assured = "5.4.0"
 managed-kotest = "5.8.0"
 managed-spock = "2.3-groovy-4.0"
 
-kotlin = "1.9.21"
+kotlin = "1.9.22"
 graal-svm = "23.1.1"
 testcontainers = "1.19.3"
 
-micronaut-test = "4.1.0"
-micronaut-data = "4.4.0"
+micronaut-test = "4.1.1"
+micronaut-data = "4.4.1"
 micronaut-hibernate-validator = "4.1.0"
 micronaut-logging = "1.1.2"
 micronaut-r2dbc = "5.2.0"
-micronaut-serde = "2.4.0"
-micronaut-spring = "5.3.0"
-micronaut-sql = "5.2.0"
-micronaut-reactor = "3.1.0"
+micronaut-serde = "2.7.0"
+micronaut-spring = "5.4.0"
+micronaut-sql = "5.4.0"
+micronaut-reactor = "3.2.1"
 
-micronaut-gradle-plugin = "4.2.0"
+micronaut-gradle-plugin = "4.2.1"
 
 [libraries]
 # Core
diff --git a/src/main/docs/guide/restAssured.adoc b/src/main/docs/guide/restAssured.adoc
index 280bd01c..25c9ba61 100644
--- a/src/main/docs/guide/restAssured.adoc
+++ b/src/main/docs/guide/restAssured.adoc
@@ -10,3 +10,4 @@ You can then inject instances of https://www.javadoc.io/static/io.rest-assured/r
 include::{restassuredtests}/RestAssuredHelloWorldTest.java[]
 ----
 
+TIP: See the guides for https://guides.micronaut.io/latest/micronaut-rest-assured.html[Using Micronaut Test REST-assured in a Micronaut Application] and https://guides.micronaut.io/latest/testing-rest-api-integrations-using-mockserver.html[Testing REST API Integrations Using Testcontainers with WireMock or MockServer] to learn more.
diff --git a/src/main/docs/guide/toc.yml b/src/main/docs/guide/toc.yml
index 30cc4eb4..d4a3779c 100644
--- a/src/main/docs/guide/toc.yml
+++ b/src/main/docs/guide/toc.yml
@@ -24,6 +24,7 @@ junit5:
   junit5DefiningAdditionalTestSpecificProperties: Defining Additional Test Specific Properties
   junit5RefreshingInjectedBeansBasedOnRequiresUponPropertiesChanges: Refreshing injected beans based on `@Requires` upon properties changes
   junit5TestingExternalServers: Testing External Servers
+  junit5TestMethodsInjection: Test Methods Injection
 kotest5:
   title: Testing with Kotest 5
   settingUpKoTest5: Setting up Kotest 5


Test patch:
diff --git a/src/main/docs/guide/junit5/junit5TestMethodsInjection.adoc b/src/main/docs/guide/junit5/junit5TestMethodsInjection.adoc
new file mode 100644
index 00000000..e3942a14
--- /dev/null
+++ b/src/main/docs/guide/junit5/junit5TestMethodsInjection.adoc
@@ -0,0 +1,9 @@
+By default, with JUnit 5 the test method parameters will be resolved to beans if possible. As this behaviour can be problematic if in combination with the `@ParameterizedTest` annotation, it can be disabled.
+
+[source,java]
+----
+include::{junit5tests}/FooTest.java[]
+----
+
+<1> Is executed with parameters that are generated by the `fooArgs` method
+<2> Throws exception since method injection is completely disabled by `resolveParameters = false`
diff --git a/test-junit5/src/main/java/io/micronaut/test/extensions/junit5/MicronautJunit5Extension.java b/test-junit5/src/main/java/io/micronaut/test/extensions/junit5/MicronautJunit5Extension.java
index 5bc01f7d..7605d89b 100644
--- a/test-junit5/src/main/java/io/micronaut/test/extensions/junit5/MicronautJunit5Extension.java
+++ b/test-junit5/src/main/java/io/micronaut/test/extensions/junit5/MicronautJunit5Extension.java
@@ -338,7 +338,7 @@ public class MicronautJunit5Extension extends AbstractMicronautExtension<Extensi
     @Override
     public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
         if (this.testAnnotationValue != null) {
-            if (!this.testAnnotationValue.isResolveParameters()) {
+            if (!this.testAnnotationValue.isResolveParameters() && parameterContext.getDeclaringExecutable() instanceof Method) {
                 return false;
             }
 
diff --git a/test-junit5/src/test/java/io/micronaut/test/junit5/FooTest.java b/test-junit5/src/test/java/io/micronaut/test/junit5/DisableResolveParametersTest.java
similarity index 55%
rename from test-junit5/src/test/java/io/micronaut/test/junit5/FooTest.java
rename to test-junit5/src/test/java/io/micronaut/test/junit5/DisableResolveParametersTest.java
index e3602e74..796c70de 100644
--- a/test-junit5/src/test/java/io/micronaut/test/junit5/FooTest.java
+++ b/test-junit5/src/test/java/io/micronaut/test/junit5/DisableResolveParametersTest.java
@@ -1,27 +1,38 @@
 package io.micronaut.test.junit5;
 
-import java.util.stream.Stream;
-
+import io.micronaut.context.annotation.Property;
+import io.micronaut.context.annotation.Requires;
 import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
-import jakarta.inject.Singleton;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
 
-@MicronautTest(resolveParameters = false)
-class FooTest {
+import jakarta.inject.Singleton;
+import java.util.stream.Stream;
 
+@Property(name = "spec.name", value = "ResolveParametersTest")
+@MicronautTest(resolveParameters = false)
+class DisableResolveParametersTest {
     static Stream<Arguments> fooArgs() {
         return Stream.of(Arguments.of(new Foo()));
     }
 
     @ParameterizedTest
     @MethodSource("fooArgs")
-    void foo(Foo arg) {
+    void foo(Foo arg) { // <1>
+        Assertions.assertNotNull(arg);
+    }
+
+    @Test
+    @Disabled("Doesn't work with resolverParameters set to false") // <2>
+    void bar(Foo arg) {
         Assertions.assertNotNull(arg);
     }
 
+    @Requires(property = "spec.name", value = "ResolveParametersTest")
     @Singleton
     static class Foo {
     }
diff --git a/test-junit5/src/test/java/io/micronaut/test/junit5/ResolveParametersTest.java b/test-junit5/src/test/java/io/micronaut/test/junit5/ResolveParametersTest.java
new file mode 100644
index 00000000..47c60023
--- /dev/null
+++ b/test-junit5/src/test/java/io/micronaut/test/junit5/ResolveParametersTest.java
@@ -0,0 +1,30 @@
+package io.micronaut.test.junit5;
+
+import io.micronaut.context.annotation.Property;
+import io.micronaut.context.annotation.Requires;
+import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
+import jakarta.inject.Singleton;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+@Property(name = "spec.name", value = "ResolveParametersTest")
+@MicronautTest
+class ResolveParametersTest {
+
+    @Test
+    void bar(Foo arg) {
+        Assertions.assertNotNull(arg);
+    }
+
+    @Requires(property = "spec.name", value = "ResolveParametersTest")
+    @Singleton
+    static class Foo {
+    }
+}
+