Markus mentioned something about him needing a method that compares two objects' getter method return values via reflection. I decided to hack together a basic implementation of an abstract JUnit TestCase:
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import junit.framework.TestCase;
public abstract class ReflectionTestCase extends TestCase {
protected void assertBeansAreEqual(Object bean1, Object bean2) {
Method[] methods1 = sort(getters(bean1.getClass().getMethods()));
Method[] methods2 = sort(getters(bean2.getClass().getMethods()));
assertMutualContainment(methods1, methods2);
assertGettersReturnSameValues(bean1, methods1, bean2, methods2);
}
protected Method[] sort(Method[] methods) {
Arrays.sort(methods, new Comparator() {
public int compare(Object o1, Object o2) {
Method m1 = (Method) o1;
Method m2 = (Method) o2;
return m1.getName().compareTo(m2.getName());
}
});
return methods;
}
protected void assertGettersReturnSameValues(Object bean1,
Method[] methods1, Object bean2, Method[] methods2) {
for (int i = 0; i < methods1.length; i++) {
try {
Object value1 = methods1[i].invoke(bean1, null);
Object value2 = methods2[i].invoke(bean2, null);
assertEquals("Method " + methods1[i].getName() + " on " + bean1
+ " doesn't return the same as method "
+ methods2[i].getName() + " on " + bean2, value1,
value2);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
protected Method[] getters(Method[] methods) {
List getters = new ArrayList();
for (int i = 0; i < methods.length; i++) {
if (takesParameters(methods[i])) {
continue;
}
if (methods[i].getName().equals("getClass")) {
continue;
}
if (methods[i].getName().matches("(get[A-Z]).*")) {
getters.add(methods[i]);
}
}
return (Method[]) getters.toArray(new Method[getters.size()]);
}
private boolean takesParameters(Method method) {
return method.getParameterTypes().length > 0;
}
protected void assertMutualContainment(Method[] array1, Method[] array2) {
for (int i = 0; i < array1.length; i++) {
assertContains(array1[i], array2);
}
for (int i = 0; i < array2.length; i++) {
assertContains(array2[i], array1);
}
}
protected void assertContains(Method needle, Method[] haystack) {
for (int i = 0; i < haystack.length; i++) {
if (haystack[i].getName().equals(needle.getName())) {
return;
}
}
fail(toString(haystack) + " doesn't contain " + needle);
}
protected String toString(Object[] array) {
StringBuffer s = new StringBuffer();
s.append("[");
for (int i = 0; i < array.length; i++) {
if (i > 0) {
s.append(", ");
}
s.append(array[i]);
}
s.append("]");
return s.toString();
}
}
...and of course I've got a TestCase for testing the TestCase:
import java.lang.reflect.Method;
import junit.framework.AssertionFailedError;
public class TestReflectionTestCaseAndGetters extends ReflectionTestCase {
private FirstBean bean1;
private FirstBean bean2;
private SecondBean bean3;
private ThirdBean bean4;
private static class FirstBean {
private String stringField;
private int primitiveField;
public int getPrimitiveField() {
return primitiveField;
}
public void setPrimitiveField(int value) {
this.primitiveField = value;
}
public String getStringField() {
return stringField;
}
public void setStringField(String value) {
this.stringField = value;
}
}
private static class SecondBean {
private String stringField;
private int primitiveField;
public String getStringField() {
return stringField;
}
public void setStringField(String value) {
this.stringField = value;
}
public String getThisIsNotGetter(String becauseOfMe) {
return "I am NOT a getter";
}
public int getPrimitiveField() {
return primitiveField;
}
public void setPrimitiveField(int value) {
this.primitiveField = value;
}
}
private static class ThirdBean {
private Object nobodyHasThisFieldExceptMe;
private String stringField;
private int primitiveField;
public int getPrimitiveField() {
return primitiveField;
}
public void setPrimitiveField(int value) {
this.primitiveField = value;
}
public String getStringField() {
return stringField;
}
public void setStringField(String value) {
this.stringField = value;
}
public void setNobodyHasThisFieldExceptMe(Object value) {
this.nobodyHasThisFieldExceptMe = value;
}
public Object getNobodyHasThisFieldExceptMe() {
return nobodyHasThisFieldExceptMe;
}
}
protected void setUp() throws Exception {
bean1 = new FirstBean();
bean2 = new FirstBean();
bean3 = new SecondBean();
bean4 = new ThirdBean();
}
public void testFilteringGetters() throws Exception {
Method[] getters = getters(SecondBean.class.getMethods());
for (int i = 0; i < getters.length; i++) {
assertTrue(
"Method "
+ getters[i].getName()
+ " shouldn't be considered a getter because it doesn't start with 'get'",
getters[i].getName().startsWith("get"));
assertEquals(
"Method "
+ getters[i].getName()
+ " shouldn't be considered a getter because it takes arguments",
0, getters[i].getParameterTypes().length);
}
}
public void testAllNullFields() throws Exception {
try {
assertBeansAreEqual(bean1, bean2);
} catch (AssertionFailedError notExpected) {
fail("Two beans with all null fields should be equal");
}
}
public void testStringFieldsWithEqualValues() throws Exception {
bean1.setStringField("foo");
bean2.setStringField("foo");
try {
assertBeansAreEqual(bean1, bean2);
} catch (AssertionFailedError notExpected) {
fail("Two beans with same values for string fields should be equal");
}
}
public void testStringFieldsWithDifferentValues() throws Exception {
bean1.setStringField("foo");
bean2.setStringField("bar");
try {
assertBeansAreEqual(bean1, bean2);
} catch (AssertionFailedError expected) {
return;
}
fail("Two beans with different values for string fields should be considered not equal");
}
public void testPrimitiveFieldsWithEqualValues() throws Exception {
bean1.setPrimitiveField(1);
bean2.setPrimitiveField(1);
try {
assertBeansAreEqual(bean1, bean2);
} catch (AssertionFailedError notExpected) {
fail("Two beans with same values for primitive fields should be equal");
}
}
public void testPrimitiveFieldsWithDifferentValues() throws Exception {
bean1.setPrimitiveField(1);
bean2.setPrimitiveField(2);
try {
assertBeansAreEqual(bean1, bean2);
} catch (AssertionFailedError expected) {
return;
}
fail("Two beans with different values for primitive fields should not be considered equal");
}
public void testAllNullFieldsOnSimilarButDifferentClasses()
throws Exception {
try {
assertBeansAreEqual(bean1, bean3);
} catch (AssertionFailedError notExpected) {
fail("Two beans with all null fields should be equal");
}
}
public void testStringFieldsWithEqualValuesOnSimilarButDifferentClasses()
throws Exception {
bean1.setStringField("foo");
bean3.setStringField("foo");
try {
assertBeansAreEqual(bean1, bean3);
} catch (AssertionFailedError notExpected) {
notExpected.printStackTrace();
fail("Two beans with same values for string fields should be equal");
}
}
public void testStringFieldsWithDifferentValuesOnSimilarButDifferentClasses()
throws Exception {
bean1.setStringField("foo");
bean3.setStringField("bar");
try {
assertBeansAreEqual(bean1, bean3);
} catch (AssertionFailedError expected) {
return;
}
fail("Two beans with different values for string fields should not be considered equal");
}
public void testPrimitiveFieldsWithEqualValuesOnSimilarButDifferentClasses()
throws Exception {
bean1.setPrimitiveField(1);
bean3.setPrimitiveField(1);
try {
assertBeansAreEqual(bean1, bean3);
} catch (AssertionFailedError notExpected) {
fail("Two beans with same values for primitive fields should be equal");
}
}
public void testPrimitiveFieldsWithDifferentValuesOnSimilarButDifferentClasses()
throws Exception {
bean1.setPrimitiveField(1);
bean3.setPrimitiveField(2);
try {
assertBeansAreEqual(bean1, bean3);
} catch (AssertionFailedError expected) {
return;
}
fail("Two beans with different values for primitive fields should not be considered equal");
}
public void testBeansWithMismatchingGetterMethodsShouldNotBeConsideredEqual()
throws Exception {
bean1.setPrimitiveField(1);
bean4.setPrimitiveField(1);
try {
assertBeansAreEqual(bean1, bean4);
} catch (AssertionFailedError expected) {
return;
}
fail("Two beans with different values for primitive fields should not be considered equal");
}
}
PS. I'd love to hear about it if you find holes from my tests (i.e. if you figure out scenarios where the assertion doesn't work as one would expect). I'm pretty sure there are some...
TrackBacks[0]
Comments[1]
Posted by lasse on July 21, 2005 11:15:21 PM EEST







