forked from codebox/javabean-tester
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathJavaBeanTester.java
154 lines (129 loc) · 6.35 KB
/
JavaBeanTester.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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 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. //TODO add method to test only listed props
*
* @param <T> 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 <T> void test(final Class<T> clazz, final String... skipThese) throws IntrospectionException {
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;
}
}
findBooleanIsMethods(clazz, prop);
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 );
} catch (Exception ex){
fail(String.format("An exception was thrown while testing the property %s: %s", prop.getName(), ex.toString()));
}
}
}
}
}
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';
// 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
}
}
/**
* Hunt down missing Boolean read method if needed as Introspector cannot find 'is' getters
* for Boolean type properties.
*
* @param clazz
* the type being introspected
* @param descriptor
* the property descriptor found so far
*/
public static <T> void findBooleanIsMethods(Class<T> clazz, PropertyDescriptor descriptor) throws IntrospectionException {
if ( needToFindReadMethod(descriptor) ) {
findTheReadMethod(descriptor, clazz);
}
}
private static boolean needToFindReadMethod(PropertyDescriptor property) {
return property.getReadMethod() == null && property.getPropertyType() == Boolean.class;
}
private static <T> void findTheReadMethod(PropertyDescriptor descriptor, Class<T> clazz) {
try {
PropertyDescriptor pd = new PropertyDescriptor(descriptor.getName(), clazz);
descriptor.setReadMethod(pd.getReadMethod());
} catch (IntrospectionException e) {}
}
}