All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fitbur.mockito.internal.configuration.injection.PropertyAndSetterInjection Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
/*
 * Copyright (c) 2007 Mockito contributors
 * This program is made available under the terms of the MIT License.
 */

package com.fitbur.mockito.internal.configuration.injection;

import static com.fitbur.mockito.exceptions.Reporter.cannotInitializeForInjectMocksAnnotation;
import static com.fitbur.mockito.exceptions.Reporter.fieldInitialisationThrewException;
import static com.fitbur.mockito.internal.util.collections.Sets.newMockSafeHashSet;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.fitbur.mockito.exceptions.base.MockitoException;
import com.fitbur.mockito.internal.configuration.injection.filter.MockCandidateFilter;
import com.fitbur.mockito.internal.configuration.injection.filter.NameBasedCandidateFilter;
import com.fitbur.mockito.internal.configuration.injection.filter.TerminalMockCandidateFilter;
import com.fitbur.mockito.internal.configuration.injection.filter.TypeBasedCandidateFilter;
import com.fitbur.mockito.internal.util.collections.ListUtil;
import com.fitbur.mockito.internal.util.reflection.FieldInitializationReport;
import com.fitbur.mockito.internal.util.reflection.FieldInitializer;
import com.fitbur.mockito.internal.util.reflection.SuperTypesLastSorter;

/**
 * Inject mocks using first setters then fields, if no setters available.
 *
 * 

* Algorithm :
* for each field annotated by @InjectMocks *

    *
  • initialize field annotated by @InjectMocks *
  • for each fields of a class in @InjectMocks type hierarchy *
      *
    • make a copy of mock candidates *
    • order fields from sub-type to super-type, then by field name *
    • for the list of fields in a class try two passes of : *
        *
      • find mock candidate by type *
      • if more than *one* candidate find mock candidate on name *
      • if one mock candidate then *
          *
        • set mock by property setter if possible *
        • else set mock by field injection *
        *
      • remove mock from mocks copy (mocks are just injected once in a class) *
      • remove injected field from list of class fields *
      *
    • else don't fail, user will then provide dependencies *
    *
*

* *

* Note: If the field needing injection is not initialized, the strategy tries * to create one using a no-arg constructor of the field type. *

*/ public class PropertyAndSetterInjection extends MockInjectionStrategy { private final MockCandidateFilter mockCandidateFilter = new TypeBasedCandidateFilter( new NameBasedCandidateFilter( new TerminalMockCandidateFilter())); private final ListUtil.Filter notFinalOrStatic = new ListUtil.Filter() { public boolean isOut(Field object) { return Modifier.isFinal(object.getModifiers()) || Modifier.isStatic(object.getModifiers()); } }; public boolean processInjection(Field injectMocksField, Object injectMocksFieldOwner, Set mockCandidates) { FieldInitializationReport report = initializeInjectMocksField(injectMocksField, injectMocksFieldOwner); // for each field in the class hierarchy boolean injectionOccurred = false; Class fieldClass = report.fieldClass(); Object fieldInstanceNeedingInjection = report.fieldInstance(); while (fieldClass != Object.class) { injectionOccurred |= injectMockCandidates(fieldClass, fieldInstanceNeedingInjection, newMockSafeHashSet(mockCandidates)); fieldClass = fieldClass.getSuperclass(); } return injectionOccurred; } private FieldInitializationReport initializeInjectMocksField(Field field, Object fieldOwner) { try { return new FieldInitializer(fieldOwner, field).initialize(); } catch (MockitoException e) { if(e.getCause() instanceof InvocationTargetException) { Throwable realCause = e.getCause().getCause(); throw fieldInitialisationThrewException(field, realCause); } throw cannotInitializeForInjectMocksAnnotation(field.getName(), e); } } private boolean injectMockCandidates(Class awaitingInjectionClazz, Object injectee, Set mocks) { boolean injectionOccurred; List orderedCandidateInjecteeFields = orderedInstanceFieldsFrom(awaitingInjectionClazz); // pass 1 injectionOccurred = injectMockCandidatesOnFields(mocks, injectee, false, orderedCandidateInjecteeFields); // pass 2 injectionOccurred |= injectMockCandidatesOnFields(mocks, injectee, injectionOccurred, orderedCandidateInjecteeFields); return injectionOccurred; } private boolean injectMockCandidatesOnFields(Set mocks, Object injectee, boolean injectionOccurred, List orderedCandidateInjecteeFields) { for (Iterator it = orderedCandidateInjecteeFields.iterator(); it.hasNext(); ) { Field candidateField = it.next(); Object injected = mockCandidateFilter.filterCandidate(mocks, candidateField, orderedCandidateInjecteeFields, injectee) .thenInject(); if (injected != null) { injectionOccurred |= true; mocks.remove(injected); it.remove(); } } return injectionOccurred; } private List orderedInstanceFieldsFrom(Class awaitingInjectionClazz) { List declaredFields = Arrays.asList(awaitingInjectionClazz.getDeclaredFields()); declaredFields = ListUtil.filter(declaredFields, notFinalOrStatic); return new SuperTypesLastSorter().sort(declaredFields); } }