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