mockit.internal.expectations.injection.TestedClassInstantiations Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmockit Show documentation
Show all versions of jmockit Show documentation
JMockit is a Java toolkit for developer (unit/integration) testing.
It contains mocking APIs and other tools, supporting both JUnit and TestNG.
The mocking APIs allow all kinds of Java code, without testability restrictions, to be tested
in isolation from selected dependencies.
/*
* Copyright (c) 2006-2012 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.expectations.injection;
import java.lang.reflect.*;
import java.util.*;
import static java.lang.reflect.Modifier.*;
import static mockit.internal.util.Utilities.*;
import mockit.*;
import mockit.internal.expectations.mocking.*;
import mockit.internal.state.*;
import mockit.internal.util.*;
public final class TestedClassInstantiations
{
private final List testedFields;
private final List injectableFields;
private List injectables;
private final List consumedInjectables;
private Object testClassInstance;
private Type typeOfInjectionPoint;
public TestedClassInstantiations()
{
testedFields = new LinkedList();
injectableFields = new ArrayList();
consumedInjectables = new ArrayList();
}
public boolean findTestedAndInjectableFields(Class> testClass)
{
new ParameterNameExtractor(true).extractNames(testClass);
Field[] fieldsInTestClass = testClass.getDeclaredFields();
for (Field field : fieldsInTestClass) {
if (field.isAnnotationPresent(Tested.class)) {
testedFields.add(field);
}
else {
MockedType mockedType = new MockedType(field, true);
if (mockedType.injectable) {
injectableFields.add(mockedType);
}
}
}
return !testedFields.isEmpty();
}
public void assignNewInstancesToTestedFields(Object testClassInstance)
{
this.testClassInstance = testClassInstance;
buildListsOfInjectables();
TestedObjectCreation testedObjectCreation = null;
for (Field testedField : testedFields) {
Object testedObject = getFieldValue(testedField, testClassInstance);
if (testedObject == null && !isFinal(testedField.getModifiers())) {
if (testedObjectCreation == null) {
testedObjectCreation = new TestedObjectCreation();
}
testedObject = testedObjectCreation.create(testedField);
setFieldValue(testedField, testClassInstance, testedObject);
}
if (testedObject != null) {
new FieldInjection().injectIntoFieldsThatAreStillNull(testedObject);
}
consumedInjectables.clear();
}
}
private void buildListsOfInjectables()
{
ParameterTypeRedefinitions paramTypeRedefs = TestRun.getExecutingTest().getParameterTypeRedefinitions();
if (paramTypeRedefs == null) {
injectables = injectableFields;
}
else {
injectables = new ArrayList(injectableFields);
injectables.addAll(paramTypeRedefs.getInjectableParameters());
}
}
private MockedType findInjectable(String nameOfInjectionPoint)
{
boolean multipleInjectablesFound = false;
MockedType found = null;
for (MockedType injectable : injectables) {
if (injectable.declaredType == typeOfInjectionPoint) {
if (found == null) {
found = injectable;
}
else {
multipleInjectablesFound = true;
if (nameOfInjectionPoint.equals(injectable.mockId)) {
return injectable;
}
}
}
}
if (multipleInjectablesFound && !nameOfInjectionPoint.equals(found.mockId)) {
return null;
}
return found;
}
private Object getValueToInject(MockedType injectable)
{
if (consumedInjectables.contains(injectable)) {
return null;
}
Object value = injectable.getValueToInject(testClassInstance);
if (value != null) {
consumedInjectables.add(injectable);
}
return value;
}
private final class TestedObjectCreation
{
private Class> testedClass;
private Constructor> constructor;
private List injectablesForConstructor;
private Type[] parameterTypes;
Object create(Field testedField)
{
testedClass = testedField.getType();
new ConstructorSearch().findSingleConstructorAccordingToClassVisibilityAndAvailableInjectables();
if (constructor == null) {
throw new IllegalArgumentException(
"No constructor in " + testedClass + " that can be satisfied by available injectables");
}
return new ConstructorInjection().instantiate();
}
MockedType findNextInjectableForVarargsParameter()
{
for (MockedType injectable : injectables) {
if (injectable.declaredType == typeOfInjectionPoint && !consumedInjectables.contains(injectable)) {
return injectable;
}
}
return null;
}
private final class ConstructorSearch
{
private final String testedClassDesc;
ConstructorSearch()
{
testedClassDesc = new ParameterNameExtractor(false).extractNames(testedClass);
injectablesForConstructor = new ArrayList();
}
void findSingleConstructorAccordingToClassVisibilityAndAvailableInjectables()
{
constructor = null;
boolean publicClass = isPublic(testedClass.getModifiers());
Constructor>[] constructors =
publicClass ? testedClass.getConstructors() : testedClass.getDeclaredConstructors();
for (Constructor> c : constructors) {
if (publicClass || !isPrivate(c.getModifiers())) {
List injectablesFound = findAvailableInjectablesForConstructor(c);
if (injectablesFound != null && injectablesFound.size() >= injectablesForConstructor.size()) {
injectablesForConstructor = injectablesFound;
constructor = c;
}
}
}
}
private List findAvailableInjectablesForConstructor(Constructor> candidate)
{
parameterTypes = candidate.getGenericParameterTypes();
int n = parameterTypes.length;
List injectablesFound = new ArrayList(n);
boolean varArgs = candidate.isVarArgs();
if (varArgs) {
n--;
}
String constructorDesc = "" + mockit.external.asm4.Type.getConstructorDescriptor(candidate);
for (int i = 0; i < n; i++) {
typeOfInjectionPoint = parameterTypes[i];
String parameterName = ParameterNames.getName(testedClassDesc, constructorDesc, i);
MockedType injectable = findInjectable(parameterName);
if (injectable == null) {
return null;
}
injectablesFound.add(injectable);
}
if (varArgs) {
MockedType injectable = hasInjectedValuesForVarargsParameter(n);
if (injectable != null) {
injectablesFound.add(injectable);
}
}
return injectablesFound;
}
private MockedType hasInjectedValuesForVarargsParameter(int varargsParameterIndex)
{
typeOfInjectionPoint = ((Class>) parameterTypes[varargsParameterIndex]).getComponentType();
return findNextInjectableForVarargsParameter();
}
}
private final class ConstructorInjection
{
Object instantiate()
{
parameterTypes = constructor.getGenericParameterTypes();
int n = parameterTypes.length;
Object[] arguments = new Object[n];
boolean varArgs = constructor.isVarArgs();
if (varArgs) {
n--;
}
for (int i = 0; i < n; i++) {
MockedType injectable = injectablesForConstructor.get(i);
arguments[i] = getArgumentValueToInject(injectable);
}
if (varArgs) {
arguments[n] = obtainInjectedVarargsArray(n);
}
return invoke(constructor, arguments);
}
private Object obtainInjectedVarargsArray(int varargsIndex)
{
typeOfInjectionPoint = ((Class>) parameterTypes[varargsIndex]).getComponentType();
List