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:
Lets use `isSame` here, as we literally want this object to be there.
Good catch, have changed it. Thanks!
I have amended the previous commit as it's a simple and small change

Patch that resolved the issue:
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 e8e576bd3d..349217474a 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,6 +5,9 @@
 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;
@@ -15,10 +18,6 @@
 import org.mockito.junit.MockitoJUnitRunner;
 import org.mockito.quality.Strictness;
 
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Optional;
-
 import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.create;
 import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;
 
@@ -101,7 +100,7 @@ public void postProcessTestInstance(Object testInstance, ExtensionContext contex
      */
     @Override
     public void beforeEach(final ExtensionContext context) {
-        List<Object> testInstances = new LinkedList<>();
+        Set<Object> testInstances = new LinkedHashSet<>();
         testInstances.add(context.getRequiredTestInstance());
 
         this.collectParentTestInstances(context, testInstances);
@@ -135,7 +134,7 @@ private Optional<MockitoSettings> retrieveAnnotationFromTestClasses(final Extens
         return annotation;
     }
 
-    private void collectParentTestInstances(ExtensionContext context, List<Object> testInstances) {
+    private void collectParentTestInstances(ExtensionContext context, Set<Object> testInstances) {
         Optional<ExtensionContext> parent = context.getParent();
 
         while (parent.isPresent() && parent.get() != context.getRoot()) {


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 3ce8c9c8f7..47361589a5 100644
--- a/subprojects/junit-jupiter/src/test/java/org/mockitousage/JunitJupiterTest.java
+++ b/subprojects/junit-jupiter/src/test/java/org/mockitousage/JunitJupiterTest.java
@@ -4,15 +4,15 @@
  */
 package org.mockitousage;
 
+import java.util.function.Function;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.junit.jupiter.MockitoExtension;
 
-import java.util.function.Function;
-
 import static org.assertj.core.api.Assertions.assertThat;
 
 @ExtendWith(MockitoExtension.class)
@@ -21,6 +21,9 @@ class JunitJupiterTest {
     @Mock
     private Function<Integer, String> rootMock;
 
+    @InjectMocks
+    private ClassWithDependency classWithDependency;
+
     @Test
     void ensureMockCreationWorks() {
         assertThat(rootMock).isNotNull();
@@ -70,4 +73,17 @@ void shouldWeCreateMocksInTheParentContext() {
             assertThat(rootMock).isNotNull();
         }
     }
+
+    @Test
+    void should_be_injected_correct_instance_of_mock() {
+        assertThat(classWithDependency.dependency).isSameAs(rootMock);
+    }
+
+    private static class ClassWithDependency {
+        private final Function<Integer, String> dependency;
+
+        private ClassWithDependency(Function<Integer, String> dependency) {
+            this.dependency = dependency;
+        }
+    }
 }