• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2007 Mockito contributors
3  * This program is made available under the terms of the MIT License.
4  */
5 package org.mockito.internal.configuration.injection;
6 
7 import static org.mockito.internal.exceptions.Reporter.cannotInitializeForInjectMocksAnnotation;
8 import static org.mockito.internal.exceptions.Reporter.fieldInitialisationThrewException;
9 import static org.mockito.internal.util.collections.Sets.newMockSafeHashSet;
10 import static org.mockito.internal.util.reflection.SuperTypesLastSorter.sortSuperTypesLast;
11 
12 import java.lang.reflect.Field;
13 import java.lang.reflect.InvocationTargetException;
14 import java.lang.reflect.Modifier;
15 import java.util.Arrays;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Set;
19 
20 import org.mockito.exceptions.base.MockitoException;
21 import org.mockito.internal.configuration.injection.filter.MockCandidateFilter;
22 import org.mockito.internal.configuration.injection.filter.NameBasedCandidateFilter;
23 import org.mockito.internal.configuration.injection.filter.TerminalMockCandidateFilter;
24 import org.mockito.internal.configuration.injection.filter.TypeBasedCandidateFilter;
25 import org.mockito.internal.util.collections.ListUtil;
26 import org.mockito.internal.util.reflection.FieldInitializationReport;
27 import org.mockito.internal.util.reflection.FieldInitializer;
28 
29 /**
30  * Inject mocks using first setters then fields, if no setters available.
31  *
32  * <p>
33  * <u>Algorithm :<br></u>
34  * for each field annotated by @InjectMocks
35  *   <ul>
36  *   <li>initialize field annotated by @InjectMocks
37  *   <li>for each fields of a class in @InjectMocks type hierarchy
38  *     <ul>
39  *     <li>make a copy of mock candidates
40  *     <li>order fields from sub-type to super-type, then by field name
41  *     <li>for the list of fields in a class try two passes of :
42  *         <ul>
43  *             <li>find mock candidate by type
44  *             <li>if more than <b>*one*</b> candidate find mock candidate on name
45  *             <li>if one mock candidate then
46  *                 <ul>
47  *                     <li>set mock by property setter if possible
48  *                     <li>else set mock by field injection
49  *                 </ul>
50  *             <li>remove mock from mocks copy (mocks are just injected once in a class)
51  *             <li>remove injected field from list of class fields
52  *         </ul>
53  *     <li>else don't fail, user will then provide dependencies
54  *     </ul>
55  *   </ul>
56  * </p>
57  *
58  * <p>
59  * <u>Note:</u> If the field needing injection is not initialized, the strategy tries
60  * to create one using a no-arg constructor of the field type.
61  * </p>
62  */
63 public class PropertyAndSetterInjection extends MockInjectionStrategy {
64 
65     private final MockCandidateFilter mockCandidateFilter =
66             new TypeBasedCandidateFilter(
67                     new NameBasedCandidateFilter(new TerminalMockCandidateFilter()));
68 
69     private final ListUtil.Filter<Field> notFinalOrStatic =
70             new ListUtil.Filter<Field>() {
71                 @Override
72                 public boolean isOut(Field object) {
73                     return Modifier.isFinal(object.getModifiers())
74                             || Modifier.isStatic(object.getModifiers());
75                 }
76             };
77 
78     @Override
processInjection( Field injectMocksField, Object injectMocksFieldOwner, Set<Object> mockCandidates)79     public boolean processInjection(
80             Field injectMocksField, Object injectMocksFieldOwner, Set<Object> mockCandidates) {
81         FieldInitializationReport report =
82                 initializeInjectMocksField(injectMocksField, injectMocksFieldOwner);
83 
84         // for each field in the class hierarchy
85         boolean injectionOccurred = false;
86         Class<?> fieldClass = report.fieldClass();
87         Object fieldInstanceNeedingInjection = report.fieldInstance();
88         while (fieldClass != Object.class) {
89             injectionOccurred |=
90                     injectMockCandidates(
91                             fieldClass,
92                             fieldInstanceNeedingInjection,
93                             newMockSafeHashSet(mockCandidates));
94             fieldClass = fieldClass.getSuperclass();
95         }
96         return injectionOccurred;
97     }
98 
initializeInjectMocksField(Field field, Object fieldOwner)99     private FieldInitializationReport initializeInjectMocksField(Field field, Object fieldOwner) {
100         try {
101             return new FieldInitializer(fieldOwner, field).initialize();
102         } catch (MockitoException e) {
103             if (e.getCause() instanceof InvocationTargetException) {
104                 Throwable realCause = e.getCause().getCause();
105                 throw fieldInitialisationThrewException(field, realCause);
106             }
107             throw cannotInitializeForInjectMocksAnnotation(field.getName(), e.getMessage());
108         }
109     }
110 
injectMockCandidates( Class<?> awaitingInjectionClazz, Object injectee, Set<Object> mocks)111     private boolean injectMockCandidates(
112             Class<?> awaitingInjectionClazz, Object injectee, Set<Object> mocks) {
113         boolean injectionOccurred;
114         List<Field> orderedCandidateInjecteeFields =
115                 orderedInstanceFieldsFrom(awaitingInjectionClazz);
116         // pass 1
117         injectionOccurred =
118                 injectMockCandidatesOnFields(
119                         mocks, injectee, false, orderedCandidateInjecteeFields);
120         // pass 2
121         injectionOccurred |=
122                 injectMockCandidatesOnFields(
123                         mocks, injectee, injectionOccurred, orderedCandidateInjecteeFields);
124         return injectionOccurred;
125     }
126 
injectMockCandidatesOnFields( Set<Object> mocks, Object injectee, boolean injectionOccurred, List<Field> orderedCandidateInjecteeFields)127     private boolean injectMockCandidatesOnFields(
128             Set<Object> mocks,
129             Object injectee,
130             boolean injectionOccurred,
131             List<Field> orderedCandidateInjecteeFields) {
132         for (Iterator<Field> it = orderedCandidateInjecteeFields.iterator(); it.hasNext(); ) {
133             Field candidateField = it.next();
134             Object injected =
135                     mockCandidateFilter
136                             .filterCandidate(
137                                     mocks, candidateField, orderedCandidateInjecteeFields, injectee)
138                             .thenInject();
139             if (injected != null) {
140                 injectionOccurred |= true;
141                 mocks.remove(injected);
142                 it.remove();
143             }
144         }
145         return injectionOccurred;
146     }
147 
orderedInstanceFieldsFrom(Class<?> awaitingInjectionClazz)148     private List<Field> orderedInstanceFieldsFrom(Class<?> awaitingInjectionClazz) {
149         List<Field> declaredFields = Arrays.asList(awaitingInjectionClazz.getDeclaredFields());
150         declaredFields = ListUtil.filter(declaredFields, notFinalOrStatic);
151 
152         return sortSuperTypesLast(declaredFields);
153     }
154 }
155