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