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