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