Using Java Reflection to mock an object

Posted by Yan on November 10, 2015

By doing this simple program, we can learn how to use Java reflection and mock a object with Google gson annotation.

The code

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by yan.dai on 3/11/2015.
 */
public class MockObject
{
    public static <T> T fakeObject(Class<T> cls) throws IllegalAccessException, InstantiationException, InvocationTargetException
    {
        Constructor<T>[] constructors = (Constructor<T>[]) cls.getDeclaredConstructors();
        Constructor<T> constructor = null;
        for (Constructor<T> c : constructors)
        {
            if (c.getParameterCount() == 0)
            {
                constructor = c;
                break;
            }
        }

        if (constructor == null)
        {
            throw new InstantiationException("cannot find default constructor for class: " + cls.getName());
        }
        constructor.setAccessible(true);

        T mock = constructor.newInstance();
        for (Field field : cls.getDeclaredFields())
        {
            field.setAccessible(true);

            if (field.getDeclaredAnnotations() != null &&
                    field.getDeclaredAnnotations().length > 0 &&
                    field.getDeclaredAnnotations()[0].annotationType().getName().equals("com.google.gson.annotations.Expose")
            )
            {
                if (field.getType().isAssignableFrom(String.class))
                {
                    field.set(mock, "string");
                }
                else if (field.getType().isAssignableFrom(Double.class) || field.getType().isAssignableFrom(double.class))
                {
                    field.set(mock, 1.1);
                }
                else if (field.getType().isAssignableFrom(Integer.class) || field.getType().isAssignableFrom(int.class))
                {
                    field.set(mock, 0);
                }
                else if (field.getType().isAssignableFrom(Long.class) || field.getType().isAssignableFrom(long.class))
                {
                    field.set(mock, 0L);
                }
                else if (field.getType().isAssignableFrom(List.class))
                {
                    List<Object> list = new ArrayList<>();
                    ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                    Class<?> genericType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
                    Object element = mockPrimitiveType(genericType);
                    if(element == null)
                    {
                        element = fakeObject(genericType);
                    }
                    list.add(element);
                    list.add(element);
                    field.set(mock, list);
                }
                else if (field.getType().isAssignableFrom(Boolean.class) || field.getType().isAssignableFrom(boolean.class))
                {
                    field.set(mock, false);
                }
                else if (field.getType().isArray())
                {
                    Class<?> elementType = field.getType().getComponentType();
                    if (elementType.equals(Character.class) || elementType.equals(char.class))
                    {
                        System.err.println("java reflection is sick");
                    }
                    else
                    {
                        Object array = Array.newInstance(elementType, 2);
                        Array.set(array, 0, fakeObject(elementType));
                        Array.set(array, 1, fakeObject(elementType));
                        field.set(mock, array);
                    }
                }
                else
                {
                    Object ins = fakeObject(field.getType());
                    field.set(mock, ins);
                }
            }
        }
        return mock;
    }


    private static Object mockPrimitiveType(Class<?> cls)
    {
        if (cls.isAssignableFrom(String.class))
        {
            return "string";
        }
        else if (cls.isAssignableFrom(Double.class) || cls.isAssignableFrom(double.class))
        {
            return 1.1;
        }
        else if (cls.isAssignableFrom(Integer.class) || cls.isAssignableFrom(int.class))
        {
            return 0;
        }
        else if (cls.isAssignableFrom(Long.class) || cls.isAssignableFrom(long.class))
        {
            return 0L;
        }
        return null;
    }
}

The testing program:

import java.lang.reflect.InvocationTargetException;
import java.util.List;

/**
 * Created by yan.dai on 3/11/2015.
 */
public class MyObj
{
    @Expose
    private String name;
    @Expose
    private int age;
    @Expose
    private List<String> values;

    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException
    {
        MyObj configs = MockObject.fakeObject(MyObj.class);
        final GsonBuilder builder = new GsonBuilder();
        builder.excludeFieldsWithoutExposeAnnotation();
        final Gson gson = builder.create();
        System.out.println(gson.toJson(configs));
    }
}

result

{"name":"string","age":0,"values":["string","string"]}