diff --git a/Bean.java b/Bean.java new file mode 100644 index 00000000..155ee318 --- /dev/null +++ b/Bean.java @@ -0,0 +1,195 @@ +package net.codebox.javabeantester.beans; + +import java.lang.annotation.RetentionPolicy; +import java.util.Date; + +/** + * Created by rob on 13/08/2017. + */ +public class Bean { + private String stringValue; + private Object[] arrayValue; + private boolean booleanValue; + private Boolean booleanWrapperValue; + private int intValue; + private Integer intWrapperValue; + private long longValue; + private Long longWrapperValue; + private double doubleValue; + private Double doubleWrapperValue; + private float floatValue; + private Float floatWrapperValue; + private char charValue; + private Character charWrapperValue; + private RetentionPolicy enumValue; + private Date noArgConstructorObjectValue; + + private byte byteValue; + private Byte byteWrapperValue; + private List listValue; + private Set setValue; + private Map mapValue; + + public String getStringValue() { + return stringValue; + } + + public void setStringValue(String stringValue) { + this.stringValue = stringValue; + } + + public Object[] getArrayValue() { + return arrayValue; + } + + public void setArrayValue(Object[] arrayValue) { + this.arrayValue = arrayValue; + } + + public boolean isBooleanValue() { + return booleanValue; + } + + public void setBooleanValue(boolean booleanValue) { + this.booleanValue = booleanValue; + } + + public Boolean getBooleanWrapperValue() { + return booleanWrapperValue; + } + + public void setBooleanWrapperValue(Boolean booleanWrapperValue) { + this.booleanWrapperValue = booleanWrapperValue; + } + + public int getIntValue() { + return intValue; + } + + public void setIntValue(int intValue) { + this.intValue = intValue; + } + + public Integer getIntWrapperValue() { + return intWrapperValue; + } + + public void setIntWrapperValue(Integer intWrapperValue) { + this.intWrapperValue = intWrapperValue; + } + + public long getLongValue() { + return longValue; + } + + public void setLongValue(long longValue) { + this.longValue = longValue; + } + + public Long getLongWrapperValue() { + return longWrapperValue; + } + + public void setLongWrapperValue(Long longWrapperValue) { + this.longWrapperValue = longWrapperValue; + } + + public double getDoubleValue() { + return doubleValue; + } + + public void setDoubleValue(double doubleValue) { + this.doubleValue = doubleValue; + } + + public Double getDoubleWrapperValue() { + return doubleWrapperValue; + } + + public void setDoubleWrapperValue(Double doubleWrapperValue) { + this.doubleWrapperValue = doubleWrapperValue; + } + + public float getFloatValue() { + return floatValue; + } + + public void setFloatValue(float floatValue) { + this.floatValue = floatValue; + } + + public Float getFloatWrapperValue() { + return floatWrapperValue; + } + + public void setFloatWrapperValue(Float floatWrapperValue) { + this.floatWrapperValue = floatWrapperValue; + } + + public char getCharValue() { + return charValue; + } + + public void setCharValue(char charValue) { + this.charValue = charValue; + } + + public Character getCharWrapperValue() { + return charWrapperValue; + } + + public void setCharWrapperValue(Character charWrapperValue) { + this.charWrapperValue = charWrapperValue; + } + + public RetentionPolicy getEnumValue() { + return enumValue; + } + + public void setEnumValue(RetentionPolicy enumValue) { + this.enumValue = enumValue; + } + + public Date getNoArgConstructorObjectValue() { + return noArgConstructorObjectValue; + } + + public void setNoArgConstructorObjectValue(Date noArgConstructorObjectValue) { + this.noArgConstructorObjectValue = noArgConstructorObjectValue; + } + public String getByteValue() { + return byteValue; + } + + public void setByteValue(byte byteValue) { + this.byteValue = byteValue; + } + public String getByteWrapperValue() { + return byteWrapperValue; + } + + public void setByteWrapperValue(byte byteWrapperValue) { + this.byteWrapperValue = byteWrapperValue; + } + public List getListValue() { + return listValue; + } + + public void setListValue(List listValue) { + this.listValue = listValue; + } + public Set getSetValue() { + return setValue; + } + + public void setSetValue(Set setValue) { + this.setValue = setValue; + } + public Map getMapValue() { + return mapValue; + } + + public void setMapValue(Map mapValue) { + this.mapValue = mapValue; + } +} diff --git a/JavaBeanTester.java b/JavaBeanTester.java new file mode 100644 index 00000000..6d46b1e4 --- /dev/null +++ b/JavaBeanTester.java @@ -0,0 +1,158 @@ +package net.codebox.javabeantester; + +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.*; + +/** + * This helper class can be used to unit test the get/set methods of JavaBean-style Value Objects. + * + * @author rob.dawson + */ +public class JavaBeanTester { + /** + * Tests the get/set methods of the specified class. + * + * @param the type parameter associated with the class under test + * @param clazz the Class under test + * @param skipThese the names of any properties that should not be tested + * @throws IntrospectionException thrown if the Introspector.getBeanInfo() method throws this exception + * for the class under test + */ + public static int test(final Class clazz, final String... skipThese) throws IntrospectionException { + int methodsTested = 0; + + final PropertyDescriptor[] props = Introspector.getBeanInfo(clazz).getPropertyDescriptors(); + nextProp: for (PropertyDescriptor prop : props) { + // Check the list of properties that we don't want to test + for (String skipThis : skipThese) { + if (skipThis.equals(prop.getName())) { + continue nextProp; + } + } + + final Method getter = prop.getReadMethod(); + final Method setter = prop.getWriteMethod(); + + if (getter != null && setter != null){ + // We have both a get and set method for this property + final Class returnType = getter.getReturnType(); + final Class[] params = setter.getParameterTypes(); + + if (params.length == 1 && params[0] == returnType){ + // The set method has 1 argument, which is of the same type as the return type of the get method, so we can test this property + try{ + // Build a value of the correct type to be passed to the set method + Object value = buildValue(returnType); + + // Build an instance of the bean that we are testing (each property test gets a new instance) + T bean = clazz.newInstance(); + + // Call the set method, then check the same value comes back out of the get method + setter.invoke(bean, value); + + final Object expectedValue = value; + final Object actualValue = getter.invoke(bean); + + assertEquals(String.format("Failed while testing property %s", prop.getName()), expectedValue, actualValue ); + + ++methodsTested; + + } catch (Exception ex){ + fail(String.format("An exception was thrown while testing the property %s: %s", prop.getName(), ex.toString())); + } + } + } + } + return methodsTested; + } + + private static Object buildMockValue(Class clazz){ + if (!Modifier.isFinal(clazz.getModifiers())){ + // Insert a call to your favourite mocking framework here + return null; + } else { + return null; + } + } + + private static Object buildValue(Class clazz) throws InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException{ + // If we are using a Mocking framework try that first... + final Object mockedObject = buildMockValue(clazz); + if (mockedObject != null){ + return mockedObject; + } + + // Next check for a no-arg constructor + final Constructor[] ctrs = clazz.getConstructors(); + for (Constructor ctr : ctrs) { + if (ctr.getParameterTypes().length == 0) { + // The class has a no-arg constructor, so just call it + return ctr.newInstance(); + } + } + + // Specific rules for common classes + if (clazz == String.class){ + return "testvalue"; + + } else if (clazz.isArray()){ + return Array.newInstance(clazz.getComponentType(), 1); + + } else if (clazz == boolean.class || clazz == Boolean.class){ + return true; + + } else if (clazz == int.class || clazz == Integer.class) { + return 1; + + } else if (clazz == long.class || clazz == Long.class) { + return 1L; + + } else if (clazz == double.class || clazz == Double.class) { + return 1.0D; + + } else if (clazz == float.class || clazz == Float.class) { + return 1.0F; + + } else if (clazz == char.class || clazz == Character.class) { + return 'Y'; + + } else if (clazz == byte.class || clazz == Byte.class) { + return (byte) 1; + } else if (clazz == List.class) { + return new ArrayList<>(); + } else if (clazz == Map.class) { + return new HashMap<>(); + } else if (clazz == Set.class) { + return new HashSet<>(); + } else if (clazz == Date.class) { + return new Date(0); + + } else if (clazz.isEnum()) { + return clazz.getEnumConstants()[0]; + + // Add your own rules here + + } else { + fail("Unable to build an instance of class " + clazz.getName() + ", please add some code to the " + + JavaBeanTester.class.getName() + " class to do this."); + return null; // for the compiler + } + } + +} +