Repository: mockito/mockito
Issue: Improve error message when @InjectMocks is uses on an interface or enum field

By accident a tests delares a field of an interface type instead of the implementing class.  

```
@InjectMocks
public InterfaceType unitUnderTest;
```

Injection is not possible on interfaces or enums. The error message should make this clear, the currenta message that doesn't help much:

```
org.mockito.exceptions.base.MockitoException: 
Cannot instantiate @InjectMocks field named 'configurationManager'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However, I failed because: the type 'ConfigurationManager' is an interface.
Examples of correct usage of @InjectMocks:
   @InjectMocks Service service = new Service();
   @InjectMocks Service service;
   //also, don't forget about MockitoAnnotations.initMocks();
   //and... don't forget about some @Mocks for injection :)
```

The error message should be someting like:
`The field 'unitUnderTest' can not be annotated with @InjectMocks cause the type 'InterfaceType ' not a class!
`


Issue comments:

Pull request review comments:
Please don't remove these indication, lot of people would like to see this.


Patch that resolved the issue:
diff --git a/src/main/java/org/mockito/internal/configuration/injection/PropertyAndSetterInjection.java b/src/main/java/org/mockito/internal/configuration/injection/PropertyAndSetterInjection.java
index 92e5868913..5b52a71262 100644
--- a/src/main/java/org/mockito/internal/configuration/injection/PropertyAndSetterInjection.java
+++ b/src/main/java/org/mockito/internal/configuration/injection/PropertyAndSetterInjection.java
@@ -98,7 +98,7 @@ private FieldInitializationReport initializeInjectMocksField(Field field, Object
                 Throwable realCause = e.getCause().getCause();
                 throw fieldInitialisationThrewException(field, realCause);
             }
-            throw cannotInitializeForInjectMocksAnnotation(field.getName(), e);
+            throw cannotInitializeForInjectMocksAnnotation(field.getName(),e.getMessage());
         }
     }
 
diff --git a/src/main/java/org/mockito/internal/exceptions/Reporter.java b/src/main/java/org/mockito/internal/exceptions/Reporter.java
index 3a63d69ddc..8a28e67f89 100644
--- a/src/main/java/org/mockito/internal/exceptions/Reporter.java
+++ b/src/main/java/org/mockito/internal/exceptions/Reporter.java
@@ -627,16 +627,14 @@ public static MockitoException cannotInitializeForSpyAnnotation(String fieldName
                                          ""), details);
     }
 
-    public static MockitoException cannotInitializeForInjectMocksAnnotation(String fieldName, Exception details) {
-        return new MockitoException(join("Cannot instantiate @InjectMocks field named '" + fieldName + "'.",
+    public static MockitoException cannotInitializeForInjectMocksAnnotation(String fieldName, String causeMessage) {
+        return new MockitoException(join("Cannot instantiate @InjectMocks field named '" + fieldName + "'! Cause: "+causeMessage,
                                          "You haven't provided the instance at field declaration so I tried to construct the instance.",
-                                         "However, I failed because: " + details.getMessage(),
                                          "Examples of correct usage of @InjectMocks:",
                                          "   @InjectMocks Service service = new Service();",
                                          "   @InjectMocks Service service;",
-                                         "   //also, don't forget about MockitoAnnotations.initMocks();",
                                          "   //and... don't forget about some @Mocks for injection :)",
-                                         ""), details);
+                                         ""));
     }
 
     public static MockitoException atMostAndNeverShouldNotBeUsedWithTimeout() {
diff --git a/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java b/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java
index 7a49e1f6b5..fdc6975498 100644
--- a/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java
+++ b/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java
@@ -7,6 +7,7 @@
 import org.mockito.exceptions.base.MockitoException;
 import org.mockito.internal.util.MockUtil;
 
+import static java.lang.reflect.Modifier.isStatic;
 import static org.mockito.internal.util.reflection.FieldSetter.setField;
 
 import java.lang.reflect.Constructor;
@@ -68,7 +69,9 @@ private FieldInitializer(Object fieldOwner, Field field, ConstructorInstantiator
             checkNotLocal(field);
             checkNotInner(field);
             checkNotInterface(field);
+            checkNotEnum(field);
             checkNotAbstract(field);
+            
         }
         this.fieldOwner = fieldOwner;
         this.field = field;
@@ -100,8 +103,9 @@ private void checkNotLocal(Field field) {
     }
 
     private void checkNotInner(Field field) {
-        if(field.getType().isMemberClass() && !Modifier.isStatic(field.getType().getModifiers())) {
-            throw new MockitoException("the type '" + field.getType().getSimpleName() + "' is an inner class.");
+        Class<?> type = field.getType();
+        if(type.isMemberClass() && !isStatic(type.getModifiers())) {
+            throw new MockitoException("the type '" + type.getSimpleName() + "' is an inner non static class.");
         }
     }
 
@@ -113,10 +117,17 @@ private void checkNotInterface(Field field) {
 
     private void checkNotAbstract(Field field) {
         if(Modifier.isAbstract(field.getType().getModifiers())) {
-            throw new MockitoException("the type '" + field.getType().getSimpleName() + " is an abstract class.");
+            throw new MockitoException("the type '" + field.getType().getSimpleName() + "' is an abstract class.");
+        }
+    }
+    
+    private void checkNotEnum(Field field) {
+        if(field.getType().isEnum()) {
+            throw new MockitoException("the type '" + field.getType().getSimpleName() + "' is an enum.");
         }
     }
 
+
     private FieldInitializationReport acquireFieldInstance() throws IllegalAccessException {
         Object fieldInstance = field.get(fieldOwner);
         if(fieldInstance != null) {


Test patch:
diff --git a/src/test/java/org/mockitousage/annotation/MockInjectionUsingConstructorTest.java b/src/test/java/org/mockitousage/annotation/MockInjectionUsingConstructorTest.java
index 4ae5590012..a11a3a7332 100644
--- a/src/test/java/org/mockitousage/annotation/MockInjectionUsingConstructorTest.java
+++ b/src/test/java/org/mockitousage/annotation/MockInjectionUsingConstructorTest.java
@@ -5,9 +5,24 @@
 
 package org.mockitousage.annotation;
 
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import java.util.AbstractCollection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.assertj.core.api.Assertions;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.internal.TextListener;
+import org.junit.rules.ExpectedException;
 import org.junit.runner.JUnitCore;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
@@ -17,17 +32,11 @@
 import org.mockito.exceptions.base.MockitoException;
 import org.mockito.internal.util.MockUtil;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.mockitousage.IMethods;
 import org.mockitousage.examples.use.ArticleCalculator;
 import org.mockitousage.examples.use.ArticleDatabase;
 import org.mockitousage.examples.use.ArticleManager;
 
-import java.util.List;
-import java.util.Set;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.when;
-
 @RunWith(MockitoJUnitRunner.class)
 public class MockInjectionUsingConstructorTest {
 
@@ -37,8 +46,8 @@ public class MockInjectionUsingConstructorTest {
     @InjectMocks private ArticleManager articleManager;
     @Spy @InjectMocks private ArticleManager spiedArticleManager;
 
-
-//    @InjectMocks private ArticleVisitor should_be_initialized_3_times;
+    @Rule
+    public ExpectedException exception = ExpectedException.none();
 
     @Test
     public void shouldNotFailWhenNotInitialized() {
@@ -120,5 +129,91 @@ private static class ATest {
         @InjectMocks FailingConstructor failingConstructor;
     }
 
+    
+    @Test
+    public void injectMocksMustFailWithInterface() throws Exception {
+        class TestCase {
+            @InjectMocks
+            IMethods f;
+        }
+
+        exception.expect(MockitoException.class);
+        exception.expectMessage("Cannot instantiate @InjectMocks field named 'f'! Cause: the type 'IMethods' is an interface");
+
+        
+        initMocks(new TestCase());
+    }
+    
+    @Test
+    public void injectMocksMustFailWithEnum() throws Exception {
+        class TestCase {
+            @InjectMocks
+            TimeUnit f;
+        }
+
+        exception.expect(MockitoException.class);
+        exception.expectMessage("Cannot instantiate @InjectMocks field named 'f'! Cause: the type 'TimeUnit' is an enum");
+        
+        initMocks(new TestCase());
+    }
+    
+    @Test
+    public void injectMocksMustFailWithAbstractClass() throws Exception {
+        class TestCase {
+            @InjectMocks
+            AbstractCollection<?> f;
+        }
+
+        exception.expect(MockitoException.class);
+        exception.expectMessage("Cannot instantiate @InjectMocks field named 'f'! Cause: the type 'AbstractCollection' is an abstract class");
+        
+        initMocks(new TestCase());
+    }
+    
+    @Test
+    public void injectMocksMustFailWithNonStaticInnerClass() throws Exception {
+        class TestCase {
+            class InnerClass {}
+            @InjectMocks
+            InnerClass f;
+        }
+        
+
+        exception.expect(MockitoException.class);
+        exception.expectMessage("Cannot instantiate @InjectMocks field named 'f'! Cause: the type 'InnerClass' is an inner non static class");
+        
+        initMocks(new TestCase());
+    }
+    
+    static class  StaticInnerClass {}
+    @Test
+    public void injectMocksMustSucceedWithStaticInnerClass() throws Exception {
+        class TestCase {
+            @InjectMocks
+            StaticInnerClass f;
+        }
+
+        TestCase testClass = new TestCase();
+        initMocks(testClass);
+        
+        assertThat(testClass.f).isInstanceOf(StaticInnerClass.class);
+    }
+   
+    @Test
+    public void injectMocksMustSucceedWithInstance() throws Exception {
+        class TestCase {
+            @InjectMocks
+            StaticInnerClass f = new StaticInnerClass();
+        }
+
+        TestCase testClass = new TestCase();
+        StaticInnerClass original = testClass.f;
+        initMocks(testClass);
+        
+        assertThat(testClass.f).isSameAs(original);
+    }
+   
+    
+   
 
 }
diff --git a/src/test/java/org/mockitousage/basicapi/MocksCreationTest.java b/src/test/java/org/mockitousage/basicapi/MocksCreationTest.java
index 7d6ae465ba..5915fe71cd 100644
--- a/src/test/java/org/mockitousage/basicapi/MocksCreationTest.java
+++ b/src/test/java/org/mockitousage/basicapi/MocksCreationTest.java
@@ -6,7 +6,9 @@
 package org.mockitousage.basicapi;
 
 import org.junit.Test;
+import org.mockito.InjectMocks;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 import org.mockito.exceptions.base.MockitoException;
 import org.mockito.exceptions.verification.SmartNullPointerException;
 import org.mockito.internal.debugging.LocationImpl;
@@ -16,6 +18,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 import static junit.framework.TestCase.*;
 import static org.assertj.core.api.Assertions.assertThat;