Skip to content

Instantly share code, notes, and snippets.

@diversit
Created May 12, 2014 08:52
Show Gist options
  • Save diversit/229ab1c5b42e74893923 to your computer and use it in GitHub Desktop.
Save diversit/229ab1c5b42e74893923 to your computer and use it in GitHub Desktop.
Test util for to easily set private/hidden fields on objects. Supports mocked types.
package nl.malmberg.test.utils;
import java.lang.reflect.Field;
/**
* Helper class for setting properties via reflection.
* Based on Deencapsulation class of JMockit.
*/
public class Deencapsulation {
private Deencapsulation() {}
public static void setField(Object objectWithField, Object fieldValue) {
final Field field = getFieldForType(objectWithField.getClass(), fieldValue.getClass());
setField(objectWithField, fieldValue, field);
}
public static void setField(Object objectWithField, String fieldName, Object fieldValue) {
final Field field = getField(objectWithField, fieldName);
setField(objectWithField, fieldValue, field);
}
/**
* @param classWithFieldClass
* @param fieldClass
* @return First field with fieldClass in classWithFieldClass or <code>null</code> if no field found.
*/
private static Field getFieldForType(Class<?> classWithFieldClass, Class<?> fieldClass) {
Class<?> wantedType = getRealClassType(fieldClass);
for(Field declaredField : classWithFieldClass.getDeclaredFields()) {
if(declaredField.getType().equals(wantedType)) {
return declaredField;
}
}
// not found (yet)
if(classWithFieldClass.getSuperclass() != null) {
return getFieldForType(classWithFieldClass.getSuperclass(), wantedType);
} else {
return null;
}
}
/**
* Get the real class type in case a mock class is used.
* @param fieldClass
* @return The real class type.
*/
private static Class<?> getRealClassType(Class<?> fieldClass) {
if(isMockedClass(fieldClass)) {
String classname = fieldClass.getName();
String typename = classname.substring(0, classname.indexOf("$$"));
try {
return Class.forName(typename);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Cannot find class for wantedType "+typename, e);
}
} else {
return fieldClass;
}
}
/**
* Assumes a mocked classname contains '$$'.
* @param aClass
* @return <code>true</code> if is a mocked class.
*/
private static boolean isMockedClass(Class<?> aClass) {
return aClass.getName().contains("$$");
}
/**
* Set value on field.
* @param objectWithField
* @param fieldValue
* @param field
*/
private static void setField(Object objectWithField, Object fieldValue, Field field) {
if(field == null) {
throw new RuntimeException("No field available to set value on.");
}
field.setAccessible(true);
try {
field.set(objectWithField, fieldValue);
} catch (IllegalAccessException e) {
throw new RuntimeException(String.format("Error setting value for field %s on object %s.", field.getName(), objectWithField), e);
}
}
/**
* @param objectWithField
* @param fieldName
* @return the field of objectWithField (or superclass) by fieldName or null if field could not be found.
*/
private static Field getField(Object objectWithField, String fieldName) {
return getField(objectWithField.getClass(), fieldName);
}
/**
* @param objectClass
* @param fieldName
* @return the field of objectClass (or superclass) by fieldName or null if field could not be found.
*/
private static Field getField(Class<?> objectClass, String fieldName) {
Field declaredField = null;
try {
declaredField = objectClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
// might fail
} catch (SecurityException e) {
// might fail
}
if(declaredField == null && objectClass.getSuperclass() != null) {
return getField(objectClass.getSuperclass(), fieldName);
} else {
return declaredField;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment