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 *       &#064;Mock private ArticleCalculator calculator;
38 *       &#064;Mock private ArticleDatabase database;
39 *       &#064;Mock private UserProvider userProvider;
40 *
41 *       private ArticleManager manager;
42 *
43 *       &#064;Before public void setup() {
44 *           manager = new ArticleManager(userProvider, database, calculator);
45 *       }
46 *   }
47 *
48 *   public class SampleBaseTestCase {
49 *
50 *       &#064;Before public void initMocks() {
51 *           MockitoAnnotations.initMocks(this);
52 *       }
53 *   }
54 * </code></pre>
55 * <p>
56 * Read also about other annotations &#064;{@link Spy}, &#064;{@link Captor}, &#064;{@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 &#064;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 (&#064;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 &#064;Mock annotation was implemented as an inner class then users experienced problems with autocomplete features in IDEs.
70     * Hence &#064;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     *  &#064;{@link org.mockito.Mock}, &#064;{@link Spy}, &#064;{@link Captor}, &#064;{@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