mockit.internal.expectations.mocking.FieldTypeRedefinitions 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 automated developer testing.
It contains APIs for the creation of the objects to be tested, for mocking dependencies, and for faking external
APIs; JUnit (4 & 5) and TestNG test runners are supported.
It also contains an advanced code coverage tool.
/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.expectations.mocking;
import java.lang.reflect.*;
import java.util.*;
import java.util.Map.*;
import javax.annotation.*;
import static java.lang.reflect.Modifier.*;
import mockit.external.asm.*;
import mockit.internal.reflection.*;
import mockit.internal.state.*;
import mockit.internal.util.*;
@SuppressWarnings("UnnecessaryFullyQualifiedName")
public final class FieldTypeRedefinitions extends TypeRedefinitions
{
private static final int FIELD_ACCESS_MASK = Access.SYNTHETIC + Access.STATIC;
@Nonnull private final Map mockInstanceFactories;
@Nonnull private final List mockFieldsNotSet;
public FieldTypeRedefinitions(@Nonnull Class testClass)
{
mockInstanceFactories = new HashMap();
mockFieldsNotSet = new ArrayList();
TestRun.enterNoMockingZone();
try {
redefineFieldTypes(testClass);
}
finally {
TestRun.exitNoMockingZone();
}
}
private void redefineFieldTypes(@Nonnull Class classWithMockFields)
{
Class superClass = classWithMockFields.getSuperclass();
if (superClass != null && superClass != Object.class && superClass != mockit.Expectations.class) {
redefineFieldTypes(superClass);
}
Field[] fields = classWithMockFields.getDeclaredFields();
for (Field candidateField : fields) {
int fieldModifiers = candidateField.getModifiers();
if ((fieldModifiers & FIELD_ACCESS_MASK) == 0) {
redefineFieldType(candidateField, fieldModifiers);
}
}
}
private void redefineFieldType(@Nonnull Field field, int modifiers)
{
MockedType mockedType = new MockedType(field);
if (mockedType.isMockableType()) {
boolean partialMocking = field.isAnnotationPresent(mockit.Tested.class);
boolean needsValueToSet = !isFinal(modifiers) && !partialMocking;
redefineFieldType(mockedType, partialMocking, needsValueToSet);
if (!partialMocking) {
registerCaptureOfNewInstances(mockedType);
}
}
}
private void redefineFieldType(@Nonnull MockedType mockedType, boolean partialMocking, boolean needsValueToSet)
{
FieldTypeRedefinition typeRedefinition = new FieldTypeRedefinition(mockedType);
boolean redefined;
if (needsValueToSet) {
InstanceFactory factory = typeRedefinition.redefineType();
redefined = factory != null;
if (redefined) {
mockInstanceFactories.put(mockedType, factory);
}
}
else {
if (partialMocking) {
redefined = typeRedefinition.redefineTypeForTestedField();
}
else {
redefined = typeRedefinition.redefineTypeForFinalField();
}
if (redefined) {
mockFieldsNotSet.add(mockedType);
}
}
if (redefined) {
addTargetClass(mockedType);
}
}
private void registerCaptureOfNewInstances(@Nonnull MockedType mockedType)
{
if (mockedType.getMaxInstancesToCapture() > 0) {
if (captureOfNewInstances == null) {
captureOfNewInstances = new CaptureOfNewInstancesForFields();
}
captureOfNewInstances.registerCaptureOfNewInstances(mockedType, null);
}
}
public void assignNewInstancesToMockFields(@Nonnull Object target)
{
TestRun.getExecutingTest().clearInjectableAndNonStrictMocks();
createAndAssignNewInstances(target);
obtainAndRegisterInstancesOfFieldsNotSet(target);
}
private void createAndAssignNewInstances(@Nonnull Object target)
{
for (Entry metadataAndFactory : mockInstanceFactories.entrySet()) {
MockedType mockedType = metadataAndFactory.getKey();
InstanceFactory instanceFactory = metadataAndFactory.getValue();
Object mock = assignNewInstanceToMockField(target, mockedType, instanceFactory);
registerMock(mockedType, mock);
}
}
@Nonnull
private Object assignNewInstanceToMockField(
@Nonnull Object target, @Nonnull MockedType mockedType, @Nonnull InstanceFactory instanceFactory)
{
Field mockField = mockedType.field;
assert mockField != null;
Object mock = FieldReflection.getFieldValue(mockField, target);
if (mock == null) {
try {
mock = instanceFactory.create();
}
catch (NoClassDefFoundError e) {
StackTrace.filterStackTrace(e);
e.printStackTrace();
throw e;
}
catch (ExceptionInInitializerError e) {
StackTrace.filterStackTrace(e);
e.printStackTrace();
throw e;
}
FieldReflection.setFieldValue(mockField, target, mock);
if (mockedType.getMaxInstancesToCapture() > 0) {
assert captureOfNewInstances != null;
CaptureOfNewInstancesForFields capture = (CaptureOfNewInstancesForFields) captureOfNewInstances;
capture.resetCaptureCount(mockField);
}
}
return mock;
}
private void obtainAndRegisterInstancesOfFieldsNotSet(@Nonnull Object target)
{
for (MockedType metadata : mockFieldsNotSet) {
assert metadata.field != null;
Object mock = FieldReflection.getFieldValue(metadata.field, target);
if (mock != null) {
registerMock(metadata, mock);
}
}
}
/**
* Returns true iff the mock instance concrete class is not mocked in some test, ie it's a class
* which only appears in the code under test.
*/
public boolean captureNewInstanceForApplicableMockField(@Nonnull Object mock)
{
if (captureOfNewInstances == null) {
return false;
}
Object fieldOwner = TestRun.getCurrentTestInstance();
return captureOfNewInstances.captureNewInstance(fieldOwner, mock);
}
@Override
public void cleanUp()
{
TestRun.getExecutingTest().getCascadingTypes().clear();
super.cleanUp();
}
}