Repository: mockito/mockito
Issue: Support @Mock injection in JUnit 5 method parameters
Labels: junit5

JUnit 5 has the ability to "inject" parameters into test methods via an extensible ``ParameterResolver`` interface.  The JUnit 5 users guide provides an example that shows how such a ``ParameterResolver`` can supply mock objects as test parameters as follows (shamelessly copied from https://junit.org/junit5/docs/current/user-guide/#writing-tests-dependency-injection):

```
@ExtendWith(MockitoExtension.class)
class MyMockitoTest {

    @BeforeEach
    void init(@Mock Person person) {
        when(person.getName()).thenReturn("Dilbert");
    }

    @Test
    void simpleTestWithInjectedMock(@Mock Person person) {
        assertEquals("Dilbert", person.getName());
    }

}
```

The prototype ``MockitoExtension`` provided in the JUnit 5 samples project shows a simple implementation of the required ``supportsParameter()`` and ``resolveParameter()`` methods. (See: https://github.com/junit-team/junit5-samples/blob/r5.1.0/junit5-mockito-extension/src/main/java/com/example/mockito/MockitoExtension.java)

[ ]   Add the ability to inject mock objects into test method parameters to the official ``MockitoExtension``.  References #445

Issue comments:
I am 👍 for this feature with the following use case:

Initialization of local variable mocks. Previously this would be `Mock<Generic> mock = mock(Mock.class)`. However, this loses the type-signature of `Generic`. With parameter mocks, we can do `@Mock Mock<Generic> mock` and still maintain the generic type signature.
I should also note that I haven't personally used parameter resolution the way it's shown above.  Our team generally either injects the mock object at the class level and configures it in ``@BeforeEach`` **OR** we inject the mock object in the test method and configure it at the top of the test method's block.
Yes agreed. Rather than some "magic" shared by methods, I think a generics-type-safe replacement for local mock initialization is a lot better.
I just edited the description of this issue to include a link to the sample ``MockitoExtension`` (I had intended to provide that reference originally).  I don't know if I'd refer to the the methods sharing the parameter instance as "magic" but without looking at the code it certainly isn't clear that the parameters are the shared if the types and names match.  I definitely think that there are use-cases for this technique and the use of ``ExtensionContext.Store`` is a great way to provide references to objects from different methods.  My main concern is that when there is a name collision and the types match, you might end up with behavior that's really hard to understand (without knowing how the underlying extension works).

I'm not sure I'm a fan of the syntax you described above as it seems more redundant than ``@Mock MyWeirdThing myWeirdMock`` - I'm guessing I'm misunderstanding your intention (I get the erasure part).
In Junit 4, local mock initialization would be 

```java
class Test {
  @Test
  public void test() {
    Mock<String> mock = Mockito.mock(Mock.class);
  }
  class Mock<T> {}
}
```
This would issue a warning saying that the generic type of `String` is not correctly created by `Mockito.mock`. (Even though we know that our mocks will work)

With this feature, in JUnit 5, we can have:

```java
class Test {
  @Test
  public void test(@Mock Mock<String> mock) {
  }
  class Mock<T> {}
}
```
And the compiler will not warn any longer.
I don't remember ever seeing that warning - Perhaps because I've always used the Mockito runner or rule?
Correct. You do not get that warning when using the runner. But then you have mocks that are used in every single test. This targets the use case where you need 1 extra mock in only 1 testcase.
And if the ``MockitoExtension`` is the JUnit 5 replacement for the runner or rule, why wouldn't those warnings also be suppressed?
Because the point of Mock initialization is different. JUnit 4 only supported for attributes. We are now talking about method parameters. Hopefully when I implement this logic, it is more clear to you 😄 
I'm not trying to be argumentative ... but as a pretty heavy user of the prototype ``MockitoExtension`` in the https://github.com/junit-team/junit5-samples project we'd have a lot of tests to update with this syntax.  We'll also need to make sure the example in the users guide and (if they're kept) the code in the junit-samples project matches the official implementation.  At this point I should probably let junit-team (I wish I could use a mention here) coordinate that with you.

@sbrannen @marcphilipp @sormuras
Looking at the source of https://github.com/junit-team/junit5-samples/blob/7bf40178345d5ca837579c8ddb8c025401a98788/junit5-mockito-extension/src/main/java/com/example/mockito/MockitoExtension.java I think we should be fine. I will let you know when the PR is open so that you can test that out.

One note: the Mockito team was not involved with the development of the prototype, but I think we will approach very similar capabilities.
I was looking at https://github.com/junit-team/junit5-samples/blob/master/junit5-mockito-extension/src/test/java/com/example/mockito/MockitoExtensionWithNestedTests.java because it's got a bunch of use cases including test-local mocks and the "magic" you referred to above.  Thinking though the syntax change you're describing, I'll assume that the tests would still run but with the same warning?

I do realize that the Mockito team didn't produce the prototype and it would be unrealistic to think that you should be permanently limited by it architecturally.  I did however think that the syntax "proposed" by the prototype was consistent with the field injection that already existed in Mockito.  I'm going to assume that what is produced as the official extension will be well integrated and well maintained - and I never complain about stable tooling!
Opened #1350 with a proposal implementation.
Okay, to write down the reason I think mock sharing by parameter is not a good idea:

First of all, there is a lot of duplicate code. For every test method, you need to list the mocks, which results in duplicate. Rather, using fields for these mocks removes the duplication.

Secondly, by relying on fields, the compiler will complain when you make a typo (or change a type). With parameters, you rely on the name (String-based) and the type declared. Both are not compiler safe.

Lastly, because you can rely on fields, refactoring is a lot easier, as IDE's support renaming by field. This is not possible for parameters.

For these 3 reasons, I see field mock sharing superior to parameter mock sharing and am therefore against introducing this logic in the parameter resolution.
> For these 3 reasons, I see field mock sharing superior to parameter mock sharing and am therefore against introducing this logic in the parameter resolution.

I totally understand the rationale. 👍 
What about support for `@Spy` and `@Captor`? Should new issues be created, or can it be part of this one?
`@Spy` makes no sense, as it requires a concrete instance. `@Captor` could be done. Let's discuss that in a different issue :+1: as this issue has been resolved.
My memory was incorrect, as #1350 has NOT been merged yet.
>`@Spy` makes no sense

You are completely right 😄.

I've created #1382 for adding support for `@Captor`
There's `Mockito.spy(Class)` which would work for a method parameter.
@marcphilipp Ah, yes I see now. Not sure how useful that actually is, as I only learned today we expose that API 😂 

Pull request review comments:

Patch that resolved the issue:
diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle
index 7a3cdfa2a7..784db4b47d 100644
--- a/gradle/dependencies.gradle
+++ b/gradle/dependencies.gradle
@@ -5,7 +5,7 @@ ext {
 def versions = [:]
 
 versions.bytebuddy = '1.8.15'
-versions.junitJupiter = '5.1.0'
+versions.junitJupiter = '5.1.1'
 
 libraries.junit4 = 'junit:junit:4.12'
 libraries.junitJupiterApi = "org.junit.jupiter:junit-jupiter-api:${versions.junitJupiter}"
diff --git a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java
index 2f6757da78..91086d2a19 100644
--- a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java
+++ b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java
@@ -14,13 +14,18 @@
  * Instantiates a mock on a field annotated by {@link Mock}
  */
 public class MockAnnotationProcessor implements FieldAnnotationProcessor<Mock> {
+    @Override
     public Object process(Mock annotation, Field field) {
+        return processAnnotationForMock(annotation, field.getType(), field.getName());
+    }
+
+    public static Object processAnnotationForMock(Mock annotation, Class<?> type, String name) {
         MockSettings mockSettings = Mockito.withSettings();
         if (annotation.extraInterfaces().length > 0) { // never null
             mockSettings.extraInterfaces(annotation.extraInterfaces());
         }
         if ("".equals(annotation.name())) {
-            mockSettings.name(field.getName());
+            mockSettings.name(name);
         } else {
             mockSettings.name(annotation.name());
         }
@@ -33,6 +38,6 @@ public Object process(Mock annotation, Field field) {
 
         // see @Mock answer default value
         mockSettings.defaultAnswer(annotation.answer());
-        return Mockito.mock(field.getType(), mockSettings);
+        return Mockito.mock(type, mockSettings);
     }
 }
diff --git a/subprojects/junit-jupiter/src/main/java/org/mockito/junit/jupiter/MockitoExtension.java b/subprojects/junit-jupiter/src/main/java/org/mockito/junit/jupiter/MockitoExtension.java
index ad1d434d3d..54510daec1 100644
--- a/subprojects/junit-jupiter/src/main/java/org/mockito/junit/jupiter/MockitoExtension.java
+++ b/subprojects/junit-jupiter/src/main/java/org/mockito/junit/jupiter/MockitoExtension.java
@@ -5,19 +5,20 @@
 package org.mockito.junit.jupiter;
 
 
-import java.util.LinkedHashSet;
-import java.util.Optional;
-import java.util.Set;
-import org.junit.jupiter.api.extension.AfterEachCallback;
-import org.junit.jupiter.api.extension.BeforeEachCallback;
-import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.*;
 import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
-import org.junit.jupiter.api.extension.TestInstancePostProcessor;
+import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoSession;
+import org.mockito.internal.configuration.MockAnnotationProcessor;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.mockito.quality.Strictness;
 
+import java.lang.reflect.Parameter;
+import java.util.LinkedHashSet;
+import java.util.Optional;
+import java.util.Set;
+
 import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.create;
 import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;
 
@@ -32,7 +33,7 @@
  * public class ExampleTest {
  *
  *     &#064;Mock
- *     private List list;
+ *     private List&lt;Integer&gt; list;
  *
  *     &#064;Test
  *     public void shouldDoSomething() {
@@ -48,7 +49,7 @@
  * public class ExampleTest {
  *
  *     &#064;Mock
- *     private List list;
+ *     private List&lt;Integer&gt; list;
  *
  *     &#064;Test
  *     public void shouldDoSomething() {
@@ -56,8 +57,57 @@
  *     }
  * }
  * </code></pre>
+ *
+ * This extension also supports JUnit Jupiter's method parameters.
+ * Use parameters for initialization of mocks that you use only in that specific test method.
+ * In other words, where you would initialize local mocks in JUnit 4 by calling {@link Mockito#mock(Class)},
+ * use the method parameter. This is especially beneficial when initializing a mock with generics, as you no
+ * longer get a warning about "Unchecked assignment".
+ * Please refer to JUnit Jupiter's documentation to learn when method parameters are useful.
+ *
+ * <pre class="code"><code class="java">
+ * <b>&#064;ExtendWith(MockitoExtension.class)</b>
+ * public class ExampleTest {
+ *
+ *     &#064;Mock
+ *     private List&lt;Integer&gt; sharedList;
+ *
+ *     &#064;Test
+ *     public void shouldDoSomething() {
+ *         sharedList.add(100);
+ *     }
+ *
+ *     &#064;Test
+ *     public void hasLocalMockInThisTest(@Mock List&lt;Integer&gt; localList) {
+ *         localList.add(100);
+ *         sharedList.add(100);
+ *     }
+ * }
+ * </code></pre>
+ *
+ * Lastly, the extension supports JUnit Jupiter's constructor parameters.
+ * This allows you to do setup work in the constructor and set
+ * your fields to <code>final</code>.
+ * Please refer to JUnit Jupiter's documentation to learn when constructor parameters are useful.
+ *
+ * <pre class="code"><code class="java">
+ * <b>&#064;ExtendWith(MockitoExtension.class)</b>
+ * public class ExampleTest {
+ *
+ *      private final List&lt;Integer&gt; sharedList;
+ *
+ *      ExampleTest(&#064;Mock sharedList) {
+ *          this.sharedList = sharedList;
+ *      }
+ *
+ *      &#064;Test
+ *      public void shouldDoSomething() {
+ *          sharedList.add(100);
+ *      }
+ * }
+ * </code></pre>
  */
-public class MockitoExtension implements TestInstancePostProcessor,BeforeEachCallback, AfterEachCallback {
+public class MockitoExtension implements TestInstancePostProcessor,BeforeEachCallback, AfterEachCallback, ParameterResolver {
 
     private final static Namespace MOCKITO = create("org.mockito");
 
@@ -161,4 +211,15 @@ public void afterEach(ExtensionContext context) {
                 .finishMocking();
     }
 
+    @Override
+    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return parameterContext.isAnnotated(Mock.class);
+    }
+
+    @SuppressWarnings("ConstantConditions")
+    @Override
+    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        final Parameter parameter = parameterContext.getParameter();
+        return MockAnnotationProcessor.processAnnotationForMock(parameterContext.findAnnotation(Mock.class).get(), parameter.getType(), parameter.getName());
+    }
 }


Test patch:
diff --git a/subprojects/junit-jupiter/src/test/java/org/mockitousage/JunitJupiterTest.java b/subprojects/junit-jupiter/src/test/java/org/mockitousage/JunitJupiterTest.java
index 47361589a5..df9fce5e88 100644
--- a/subprojects/junit-jupiter/src/test/java/org/mockitousage/JunitJupiterTest.java
+++ b/subprojects/junit-jupiter/src/test/java/org/mockitousage/JunitJupiterTest.java
@@ -11,6 +11,7 @@
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.internal.util.MockUtil;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -35,6 +36,32 @@ void can_set_stubs_on_initialized_mock() {
         assertThat(rootMock.apply(10)).isEqualTo("Return");
     }
 
+    @Test
+    void initializes_parameters(@Mock Function<String, String> localMock) {
+        Mockito.when(localMock.apply("Para")).thenReturn("Meter");
+        assertThat(localMock.apply("Para")).isEqualTo("Meter");
+    }
+
+    @Test
+    void initializes_parameters_with_custom_configuration(@Mock(name = "overriddenName") Function<String, String> localMock) {
+        assertThat(MockUtil.getMockName(localMock).toString()).isEqualTo("overriddenName");
+    }
+
+    @Nested
+    class NestedTestWithConstructorParameter {
+        private final Function<Integer, String> constructorMock;
+
+        NestedTestWithConstructorParameter(@Mock Function<Integer, String> constructorMock) {
+            this.constructorMock = constructorMock;
+        }
+
+        @Test
+        void can_inject_into_constructor_parameter() {
+            Mockito.when(constructorMock.apply(42)).thenReturn("42");
+            assertThat(constructorMock.apply(42)).isEqualTo("42");
+        }
+    }
+
     @Nested
     class NestedTestWithExtraMock {
         @Mock Runnable nestedMock;