1/* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 6package org.mockito; 7 8import org.mockito.configuration.AnnotationEngine; 9import org.mockito.configuration.DefaultMockitoConfiguration; 10import org.mockito.exceptions.Reporter; 11import org.mockito.exceptions.base.MockitoException; 12import org.mockito.internal.configuration.GlobalConfiguration; 13import org.mockito.internal.util.reflection.FieldSetter; 14import org.mockito.runners.MockitoJUnitRunner; 15 16import java.lang.annotation.Annotation; 17import java.lang.annotation.Retention; 18import java.lang.annotation.RetentionPolicy; 19import java.lang.annotation.Target; 20import java.lang.reflect.Field; 21 22import static java.lang.annotation.ElementType.FIELD; 23 24/** 25 * MockitoAnnotations.initMocks(this); initializes fields annotated with Mockito annotations. 26 * <p> 27 * <ul> 28 * <li>Allows shorthand creation of objects required for testing.</li> 29 * <li>Minimizes repetitive mock creation code.</li> 30 * <li>Makes the test class more readable.</li> 31 * <li>Makes the verification error easier to read because <b>field name</b> is used to identify the mock.</li> 32 * </ul> 33 * 34 * <pre class="code"><code class="java"> 35 * public class ArticleManagerTest extends SampleBaseTestCase { 36 * 37 * @Mock private ArticleCalculator calculator; 38 * @Mock private ArticleDatabase database; 39 * @Mock private UserProvider userProvider; 40 * 41 * private ArticleManager manager; 42 * 43 * @Before public void setup() { 44 * manager = new ArticleManager(userProvider, database, calculator); 45 * } 46 * } 47 * 48 * public class SampleBaseTestCase { 49 * 50 * @Before public void initMocks() { 51 * MockitoAnnotations.initMocks(this); 52 * } 53 * } 54 * </code></pre> 55 * <p> 56 * Read also about other annotations @{@link Spy}, @{@link Captor}, @{@link InjectMocks} 57 * <p> 58 * <b><code>MockitoAnnotations.initMocks(this)</code></b> method has to called to initialize annotated fields. 59 * <p> 60 * In above example, <code>initMocks()</code> is called in @Before (JUnit4) method of test's base class. 61 * For JUnit3 <code>initMocks()</code> can go to <code>setup()</code> method of a base class. 62 * You can also put initMocks() in your JUnit runner (@RunWith) or use built-in runner: {@link MockitoJUnitRunner} 63 */ 64public class MockitoAnnotations { 65 66 /** 67 * Use top-level {@link org.mockito.Mock} annotation instead 68 * <p> 69 * When @Mock annotation was implemented as an inner class then users experienced problems with autocomplete features in IDEs. 70 * Hence @Mock was made a top-level class. 71 * <p> 72 * How to fix deprecation warnings? 73 * Typically, you can just <b>search:</b> import org.mockito.MockitoAnnotations.Mock; <b>and replace with:</b> import org.mockito.Mock; 74 * <p> 75 * If you're an existing user then sorry for making your code littered with deprecation warnings. 76 * This change was required to make Mockito better. 77 */ 78 @Target( { FIELD }) 79 @Retention(RetentionPolicy.RUNTIME) 80 @Deprecated 81 public @interface Mock {} 82 83 /** 84 * Initializes objects annotated with Mockito annotations for given testClass: 85 * @{@link org.mockito.Mock}, @{@link Spy}, @{@link Captor}, @{@link InjectMocks} 86 * <p> 87 * See examples in javadoc for {@link MockitoAnnotations} class. 88 */ 89 public static void initMocks(Object testClass) { 90 if (testClass == null) { 91 throw new MockitoException("testClass cannot be null. For info how to use @Mock annotations see examples in javadoc for MockitoAnnotations class"); 92 } 93 94 AnnotationEngine annotationEngine = new GlobalConfiguration().getAnnotationEngine(); 95 Class<?> clazz = testClass.getClass(); 96 97 //below can be removed later, when we get read rid of deprecated stuff 98 if (annotationEngine.getClass() != new DefaultMockitoConfiguration().getAnnotationEngine().getClass()) { 99 //this means user has his own annotation engine and we have to respect that. 100 //we will do annotation processing the old way so that we are backwards compatible 101 while (clazz != Object.class) { 102 scanDeprecatedWay(annotationEngine, testClass, clazz); 103 clazz = clazz.getSuperclass(); 104 } 105 } 106 107 //anyway act 'the new' way 108 annotationEngine.process(testClass.getClass(), testClass); 109 } 110 111 static void scanDeprecatedWay(AnnotationEngine annotationEngine, Object testClass, Class<?> clazz) { 112 Field[] fields = clazz.getDeclaredFields(); 113 114 for (Field field : fields) { 115 processAnnotationDeprecatedWay(annotationEngine, testClass, field); 116 } 117 } 118 119 @SuppressWarnings("deprecation") 120 static void processAnnotationDeprecatedWay(AnnotationEngine annotationEngine, Object testClass, Field field) { 121 boolean alreadyAssigned = false; 122 for(Annotation annotation : field.getAnnotations()) { 123 Object mock = annotationEngine.createMockFor(annotation, field); 124 if (mock != null) { 125 throwIfAlreadyAssigned(field, alreadyAssigned); 126 alreadyAssigned = true; 127 try { 128 new FieldSetter(testClass, field).set(mock); 129 } catch (Exception e) { 130 throw new MockitoException("Problems setting field " + field.getName() + " annotated with " 131 + annotation, e); 132 } 133 } 134 } 135 } 136 137 static void throwIfAlreadyAssigned(Field field, boolean alreadyAssigned) { 138 if (alreadyAssigned) { 139 new Reporter().moreThanOneAnnotationNotAllowed(field.getName()); 140 } 141 } 142} 143