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