Repository: mockito/mockito
Issue: DEEP_STUBS tries to mock final class

```
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>1.10.19</version>
        <scope>test</scope>
    </dependency>
```

```
$ java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
```

```
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;

import org.junit.Test;

public class MockitoBug {
  public interface Supplier<T> {
    public T get();
  }

  public interface StringSupplier extends Supplier<String> {}

  public interface InheritedSupplier extends StringSupplier {}

  @Test
  public void deepStubs() {
    StringSupplier mock = mock(StringSupplier.class, RETURNS_DEEP_STUBS);
    String s = mock.get();
    assertNull(s);
  }

  @Test
  public void inheritedDeepStubs() {
    InheritedSupplier mock = mock(InheritedSupplier.class, RETURNS_DEEP_STUBS);
    String s = mock.get();  // ClassCastException
    assertNull(s);
  }
}
```

```
java.lang.ClassCastException: org.mockito.internal.creation.cglib.ClassImposterizer$ClassWithSuperclassToWorkAroundCglibBug$$EnhancerByMockitoWithCGLIB$$322ebf6e cannot be cast to java.lang.String
    at MockitoBug.inheritedDeepStubs(MockitoBug.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
```

I expect the behavior for `StringSupplier` and `InheritedSupplier` to be the same: return `null` for `get`. However, `InheritedSupplier` tries to return a mock `Object` for `get`.


Issue comments:
I have a hacky workaround using Guava's `TypeToken`:

```
  private static final MockUtil mockUtil = new MockUtil();

  @SuppressWarnings("unchecked")
  private static <T> T createSmartDeepMock(TypeToken<T> mockType) {
    return (T) mock(mockType.getRawType(), createSmartDeepMockAnswer(mockType));
  }

  private static Answer<?> createSmartDeepMockAnswer(TypeToken<?> mockType) {
    Map<Method, Object> mocks = new LinkedHashMap<>();

    return invocation -> {
      Method method = invocation.getMethod();
      if (mocks.containsKey(method)) {
        return mocks.get(method);
      }

      Type returnType = method.getGenericReturnType();
      TypeToken<?> resolvedReturnType = mockType.resolveType(returnType);
      Class<?> returnClass = resolvedReturnType.getRawType();

      if (!mockUtil.isTypeMockable(returnClass)) {
        return Mockito.RETURNS_DEFAULTS.answer(invocation);
      } else {
        Object mock = createSmartDeepMock(resolvedReturnType);
        mocks.put(method, mock);
        return mock;
      }
    };
  }
```


Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java b/src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java
index eedfebd76b..8efd384992 100644
--- a/src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java
+++ b/src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java
@@ -251,7 +251,7 @@ public GenericMetadataSupport resolveGenericReturnType(Method method) {
     private GenericMetadataSupport resolveGenericType(Type type, Method method) {
 
         if (type instanceof Class) {
-            return new NotGenericReturnTypeSupport(type);
+            return new NotGenericReturnTypeSupport(this, type);
         }
         if (type instanceof ParameterizedType) {
             return new ParameterizedReturnType(this, method.getTypeParameters(), (ParameterizedType) type);
@@ -494,8 +494,11 @@ public Class<?> rawType() {
     private static class NotGenericReturnTypeSupport extends GenericMetadataSupport {
         private final Class<?> returnType;
 
-        public NotGenericReturnTypeSupport(Type genericReturnType) {
+        public NotGenericReturnTypeSupport(GenericMetadataSupport source, Type genericReturnType) {
             returnType = (Class<?>) genericReturnType;
+            this.contextualActualTypeParameters = source.contextualActualTypeParameters;
+
+            registerAllTypeVariables(returnType);
         }
 
         @Override


Test patch:
diff --git a/src/test/java/org/mockitousage/stubbing/DeepStubbingTest.java b/src/test/java/org/mockitousage/stubbing/DeepStubbingTest.java
index fba55af035..e20f36c29d 100644
--- a/src/test/java/org/mockitousage/stubbing/DeepStubbingTest.java
+++ b/src/test/java/org/mockitousage/stubbing/DeepStubbingTest.java
@@ -14,6 +14,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.OutputStream;
 import java.net.Socket;
+import java.util.List;
 import java.util.Locale;
 
 import static junit.framework.TestCase.*;
@@ -64,7 +65,15 @@ public String getLongName() {
     }    
     
     static final class FinalClass {}
-    
+
+    interface First {
+        Second getSecond();
+
+        String getString();
+    }
+
+    interface Second extends List<String> {}
+
     @Test
     public void myTest() throws Exception {
         SocketFactory sf = mock(SocketFactory.class, RETURNS_DEEP_STUBS);
@@ -310,4 +319,11 @@ public void shouldFailGracefullyWhenClassIsFinal() throws Exception {
         //then
         assertEquals(value, person.getFinalClass());
     }
+
+    @Test
+    public void deep_stub_does_not_try_to_mock_generic_final_classes() {
+        First first = mock(First.class, RETURNS_DEEP_STUBS);
+        assertNull(first.getString());
+        assertNull(first.getSecond().get(0));
+    }
 }