Repository: mockito/mockito
Issue: Different mocks are used for @Mock and @InjectMock in the same test class with JUnit 5 extension
Labels: bug
injection
junit5

I've been playing with the new JUnit 5 support in Mockito 2.17.0. I have found that with `@Mock` and `@InjectMocks` used in the same test class different mocks are injected. I would expect to have the same mock reused (as it works with JUnit 4 or manual `@Mock` initialization).

Sample code:
```
@ExtendWith(MockitoExtension.class)
public class SpaceShip5BugMiniTest {

    @InjectMocks
    private SpaceShip spaceShip;

    @Mock
    private TacticalStation tacticalStation;

    @Test
    public void shouldInjectMocks() {
        assertThat(tacticalStation).isNotNull();
        assertThat(spaceShip.getTacticalStation()).isEqualTo(tacticalStation);  
        //Bug ↑ - different mocks are use in @Mock and @InjectMock
    }
}
```

Error message:
```
org.opentest4j.AssertionFailedError: 
Expecting:
 <"tacticalStation (TacticalStation$MockitoMock$991606095@61d6015a)">
to be equal to:
 <"tacticalStation (TacticalStation$MockitoMock$991606095@b62d79)">
but was not.
Expected :tacticalStation
Actual   :tacticalStation
```

The minimal (non-)working example is available [here](https://github.com/szpak/code-examples-and-poc/tree/mockito2junit5-bug/mockito2-junit5).

@TimvdLippe Do you have an idea what could be wrong?

Mockito 2.17.0 (2.17.1 is not being released due to (https://github.com/mockito/shipkit/issues/679), junit-jupiter 5.1.0, OpenJDK 1.8.0_161, Linux.

### Checklist:
 - [x] The mockito message in the stacktrace have useful information, but it didn't help
 - [x] The problematic code (if that's possible) is copied here;
       Note that some configuration are impossible to mock via Mockito
 - [x] Provide versions (mockito / jdk / os / any other relevant information)
 - [x] Provide a [Short, Self Contained, Correct (Compilable), Example](http://sscce.org) of the issue
       (same as any question on stackoverflow.com)
 - [x] Read the [contributing guide](https://github.com/mockito/mockito/blob/master/.github/CONTRIBUTING.md)


Issue comments:
Hm that seems really weird. Could you please post the implementation of `TacticalStation` and `SpaceShip` (although I am assuming they are simple POJO's?)
(Oh never mind, I missed the small link at the end of the example to https://github.com/szpak/code-examples-and-poc/tree/mockito2junit5-bug/mockito2-junit5/src/main/java/info/solidsoft/blog/mockito/junit5 )
@szpak Please see https://github.com/mockito/mockito/blob/5e9a3c62fc1ca4cf12d5d19580613f418cebc895/subprojects/junit-jupiter/src/test/java/org/mockitousage/InjectMocksTest.java for a working test. I could make these tests fail with adding `final` to the field declaration, so that is what is going wrong. Removing `final` makes the tests pass.

What I do not understand is how this could work with JUnit 4, as we have not changed any of that code. We are completely reusing all of that logic.
It also works with `final` if `MockitoAnnotations.initMocks(this);` is used. Therefore, I would suspect the way how it is triggered/executed in JUnit 5 extension.
@TimvdLippe @szpak I have debugged this issue a bit. Here is what I found: it does look like the problem is with triggering the same logic from `MockitoExtension.class`. 
The root cause of the problem is that `beforeEach` creates two `testInstances` of the same test class for each test. 
Looks like method `collectParentTestInstances` is adding the same testInstance to the list of test instances.
I can try to work on this issue if no one is assigned to is already.

PRs appreciated!

On Mon, 26 Mar 2018, 10:55 Serge Bishyr, <notifications@github.com> wrote:

> @TimvdLippe <https://github.com/TimvdLippe> @szpak
> <https://github.com/szpak> I have debugged this issue a bit. Here is what
> I found: it does look like the problem is with triggering the same logic
> from MockitoExtension.class.
> The root cause of the problem is that beforeEach creates two testInstances
> of the same test class for each test.
> Looks like method collectParentTestInstances is adding the same
> testInstance to the list of test instances.
> I can try to work on this issue if no one is assigned to is already.
>
> —
> You are receiving this because you were mentioned.
>
>
> Reply to this email directly, view it on GitHub
> <https://github.com/mockito/mockito/issues/1346#issuecomment-376094135>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AFrDb9w69sJERNCP5FZJXPrwdq0ltsI9ks5tiK0ZgaJpZM4S6Huw>
> .
>

Have added PR #1349 with the fix for the issue
It works flawlessly. Thanks @SeriyBg!

@TimvdLippe As this bug can impact all those people preferring a constructor injection, what do you thing about releasing the new minor version (e.g. after #1350 is hopefully merged :) ) to Maven Central?
@szpak Yes after #1350 we will probably publish a new version. You can however already use the latest version from Bintray
I know Tim that I can, but JUnit 5 support is a notable feature and having it working correctly in the latest non-development version (available in Maven Central) seems very sensible for me. Great, that we agree on releasing a new stable (but IHMO still 2.17.x) version soon.
I'm seeing this same bug when using `SpringExtension.class` along with `MockitoExtension.class`:

```
@ExtendWith({MockitoExtension.class, SpringExtension.class})
public class InjectMocksTest {
  static class TacticalStation {}

  static class SpaceShip {
    private final TacticalStation tacticalStation;

    public SpaceShip(final TacticalStation tacticalStation) {
      this.tacticalStation = tacticalStation;
    }

    public TacticalStation getTacticalStation() {
      return tacticalStation;
    }
  }

  @InjectMocks
  private SpaceShip spaceShip;

  @Mock
  private TacticalStation tacticalStation;

  @Test
  public void shouldInjectMocks() {
    assertThat(tacticalStation).isNotNull();
    assertThat(spaceShip.getTacticalStation()).isEqualTo(tacticalStation);
    //Bug ↑ - different mocks are use in @Mock and @InjectMock
  }
}
```

This is using Mockito 3.1.0, JUnit 5.6.0, and Spring Boot 5.3.0.RELEASE.
> I'm seeing this same bug when using `SpringExtension.class` along with `MockitoExtension.class`:
> 
> ```
> @ExtendWith({MockitoExtension.class, SpringExtension.class})
> public class InjectMocksTest {
>   static class TacticalStation {}
> 
>   static class SpaceShip {
>     private final TacticalStation tacticalStation;
> 
>     public SpaceShip(final TacticalStation tacticalStation) {
>       this.tacticalStation = tacticalStation;
>     }
> 
>     public TacticalStation getTacticalStation() {
>       return tacticalStation;
>     }
>   }
> 
>   @InjectMocks
>   private SpaceShip spaceShip;
> 
>   @Mock
>   private TacticalStation tacticalStation;
> 
>   @Test
>   public void shouldInjectMocks() {
>     assertThat(tacticalStation).isNotNull();
>     assertThat(spaceShip.getTacticalStation()).isEqualTo(tacticalStation);
>     //Bug ↑ - different mocks are use in @Mock and @InjectMock
>   }
> }
> ```
> 
> This is using Mockito 3.1.0, JUnit 5.6.0, and Spring Boot 5.3.0.RELEASE.

Yes I am also facing this issue. Mine is also exactly the same scenario. Below are my dependencies,
org.junit.jupiter:junit-jupiter-engine:5.6.0
org.springframework.boot:spring-boot-starter:2.2.6.RELEASE
org.springframework.boot:spring-boot-starter-test:2.2.6.RELEASE
org.mockito:mockito-junit-jupiter:3.1.0

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;