`
niwtsew
  • 浏览: 72038 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Unit Testing Equals and HashCode of Java Beans

阅读更多

copy from http://blog.cornetdesign.com/2008/05/unit-testing-equals-and-hashcode-of-java-beans/

 

Posted on May 28th, 2008

 

We’ve been creating several Java Beans that have an imperative need to have both equals and hashCode working correctly. To do this, we started off writing a series of test cases which made sure that for every field we exposed that the objects were / were not equal based on if the field was set, and that the field was taken into account as part of the hashCode calculation. After doing two of these, I figured there had to be a better way, and after some research, ended up writing a BeanTestCase class which exposes an assertMeetsEqualsContract method and an assertMeetsHashCodeContract method. Note that while there are projects out there (like Assertion Extensions for JUnit ) that have assertions for equals and hashCode, they don’t actually walk the fields of the objects and test the various scenarios of changing each field.

After I had written this, I applied it to classes we already had unit tests for around equals and hashCode – and immediately found a bug in one of the classes where we missed a field. That paid for itself right away. ;)

Being that I’m just getting back into the Java world, if there are better ways, please let me know!

package com.cornetdesign;

import junit.framework.TestCase;

import java.lang.reflect.Field;

public class BeanTestCase extends TestCase {

    private static final String TEST_STRING_VAL1 = "Some Value";
    private static final String TEST_STRING_VAL2 = "Some Other Value";

    public static void assertMeetsEqualsContract(Class classUnderTest) {
        Object o1;
        Object o2;
        try {
            //Get Instances
            o1 = classUnderTest.newInstance();
            o2 = classUnderTest.newInstance();

            assertTrue("Instances with default constructor not equal (o1.equals(o2))", o1.equals(o2));
            assertTrue("Instances with default constructor not equal (o2.equals(o1))", o2.equals(o1));

            Field[] fields = classUnderTest.getDeclaredFields();
            for(int i = 0; i < fields.length; i++) {

                //Reset the instances
                o1 = classUnderTest.newInstance();
                o2 = classUnderTest.newInstance();

                Field field = fields[i];
                field.setAccessible(true);
                if(field.getType() == String.class) {
                    field.set(o1, TEST_STRING_VAL1);
                }  else if(field.getType() == boolean.class) {
                    field.setBoolean(o1, true);
                }   else if(field.getType() == short.class) {
                    field.setShort(o1, (short)1);
                }   else if(field.getType() == long.class) {
                    field.setLong(o1, (long)1);
                }   else if(field.getType() == float.class) {
                    field.setFloat(o1, (float)1);
                }   else if(field.getType() == int.class) {
                    field.setInt(o1, 1);
                }   else if(field.getType() == byte.class) {
                    field.setByte(o1, (byte)1);
                }   else if(field.getType() == char.class) {
                    field.setChar(o1, (char)1);
                }   else if(field.getType() == double.class) {
                    field.setDouble(o1, (double)1);
                }   else if(field.getType().isEnum()) {
                    field.set(o1, field.getType().getEnumConstants()[0]);
                } else if(Object.class.isAssignableFrom(field.getType())) {
                    field.set(o1, field.getType().newInstance());
                }   else {
                    fail("Don't know how to set a " + field.getType().getName());
                }

                assertFalse("Instances with o1 having " + field.getName() + " set and o2 having it not set are equal", o1.equals(o2));


                field.set(o2, field.get(o1));

                assertTrue("After setting o2 with the value of the object in o1, the two objects in the field are not equal"
                        , field.get(o1).equals(field.get(o2)));

                assertTrue("Instances with o1 having "
                        + field.getName()
                        + " set and o2 having it set to the same object of type "
                        + field.get(o2).getClass().getName()
                        + " are not equal", o1.equals(o2));
                
                if(field.getType() == String.class) {
                    field.set(o2, TEST_STRING_VAL2);
                }  else if(field.getType() == boolean.class) {
                    field.setBoolean(o2, false);
                }   else if(field.getType() == short.class) {
                    field.setShort(o2, (short)0);
                }   else if(field.getType() == long.class) {
                    field.setLong(o2, (long)0);
                }   else if(field.getType() == float.class) {
                    field.setFloat(o2, (float)0);
                }   else if(field.getType() == int.class) {
                    field.setInt(o2, 0);
                }   else if(field.getType() == byte.class) {
                    field.setByte(o2, (byte)0);
                }   else if(field.getType() == char.class) {
                    field.setChar(o2, (char)0);
                }   else if(field.getType() == double.class) {
                    field.setDouble(o2, (double)1);
                }   else if(field.getType().isEnum()) {
                    field.set(o2, field.getType().getEnumConstants()[1]);
                } else if(Object.class.isAssignableFrom(field.getType())) {
                    field.set(o2, field.getType().newInstance());
                }   else {
                    fail("Don't know how to set a " + field.getType().getName());
                }
                if(field.get(o1).equals(field.get(o2)))  {
                    //Even though we have different instances, they are equal. Let's walk one of them
                    //to see if we can find a field to set
                    Field[] paramFields = field.get(o1).getClass().getDeclaredFields();
                    for(int j=0; j < paramFields.length; j++) {
                        paramFields[j].setAccessible(true);
                        if(paramFields[j].getType() == String.class) {
                            paramFields[j].set(field.get(o1), TEST_STRING_VAL1);
                        }
                    }
                }

                assertFalse("After setting o2 with a different object than what is in o1, the two objects in the field are equal. "
                        + "This is after an attempt to walk the fields to make them different"
                        , field.get(o1).equals(field.get(o2)));
                assertFalse("Instances with o1 having " + field.getName() + " set and o2 having it set to a different object are equal", o1.equals(o2));
            }

        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new AssertionError("Unable to construct an instance of the class under test");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new AssertionError("Unable to construct an instance of the class under test");
        }
    }

    public void testEqualsContractMet() {
        assertMeetsEqualsContract(FakeObject.class);
    }

    public static void assertMeetsHashCodeContract(Class classUnderTest) {
        try {
            Field[] fields = classUnderTest.getDeclaredFields();
            for(int i = 0; i < fields.length; i++) {
                Object o1 = classUnderTest.newInstance();
                int initialHashCode = o1.hashCode();

                Field field = fields[i];
                field.setAccessible(true);
                if(field.getType() == String.class) {
                    field.set(o1, TEST_STRING_VAL1);
                }  else if(field.getType() == boolean.class) {
                    field.setBoolean(o1, true);
                }   else if(field.getType() == short.class) {
                    field.setShort(o1, (short)1);
                }   else if(field.getType() == long.class) {
                    field.setLong(o1, (long)1);
                }   else if(field.getType() == float.class) {
                    field.setFloat(o1, (float)1);
                }   else if(field.getType() == int.class) {
                    field.setInt(o1, 1);
                }   else if(field.getType() == byte.class) {
                    field.setByte(o1, (byte)1);
                }   else if(field.getType() == char.class) {
                    field.setChar(o1, (char)1);
                }   else if(field.getType() == double.class) {
                    field.setDouble(o1, (double)1);
                }   else if(field.getType().isEnum()) {
                    field.set(o1, field.getType().getEnumConstants()[0]);
                } else if(Object.class.isAssignableFrom(field.getType())) {
                    field.set(o1, field.getType().newInstance());
                }   else {
                    fail("Don't know how to set a " + field.getType().getName());
                }
                int updatedHashCode = o1.hashCode();
                assertFalse("The field " + field.getName() + " was not taken into account for the hashCode contract ", initialHashCode == updatedHashCode);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
            throw new AssertionError("Unable to construct an instance of the class under test");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new AssertionError("Unable to construct an instance of the class under test");
        }
    }

    public void testHashCodeContractMet() {
        assertMeetsHashCodeContract(FakeObject.class);
    }

}
 
分享到:
评论

相关推荐

    Java重写equals同时需要重写hashCode的代码说明

    Java重写equals同时需要重写hashCode的代码说明,以及如何重写hashCode方法,此代码演示按照effective java书籍说明的重写思路。代码中演示了使用集合存储对象,并且对象作为key,需重写equals和hashCode.

    重写equals和hashcode方法_equals_重写equals和hashcode方法_

    在Java编程语言中,`equals()` 和 `hashCode()` 方法是Object类中的两个核心方法,所有类都默认继承自Object类。这两个方法在处理对象比较和集合操作时起着至关重要的作用。当我们创建自定义类并需要对对象进行精确...

    equals,hashcode,toString

    在Java编程语言中,`equals()`, `hashCode()` 和 `toString()` 是三个非常重要的方法,它们主要用于对象的比较、哈希存储以及打印对象信息。这三个方法是Java对象的基础特性,对于理解和开发高质量的Java程序至关...

    equals与hashCode在实际开发中的重写写法

    在Java编程语言中,`equals()` 和 `hashCode()` 方法是两个非常重要的成员,尤其是在处理对象比较和集合操作时。这两个方法通常与`Object`类中的默认实现相关联,但为了在实际开发中实现正确的对象比较和哈希表操作...

    Java中equals,hashcode和==的区别

    "Java中equals、hashcode和==的区别" Java 中 equals、hashcode 和==的区别是 Java 编程语言中一个经常遇到的问题。这三个概念都是用来比较对象的,但是它们之间存在着本质的区别。 首先,==号是Java中的一个...

    Java容器集合(equals 和 hashCode+基础数据结构+ArrayList+Vector和LinkedList)

    Java容器集合(equals和hashCode+基础数据结构+ArrayList+Vector和LinkedList) Java容器集合是Java中的一种基础数据结构,用于存储和管理数据。其中,equals和hashCode方法是Java容器集合中两个非常重要的方法,...

    equals与hashCode方法讲解

    equals 方法和 hashCode 方法是 Java 语言中两个重要的方法,它们都是在 Object 类中定义的。equals 方法用于比较两个对象是否相等,而 hashCode 方法用于返回对象的哈希码。 在 Java 的 Object 类中,equals 方法...

    Java重写equals及hashcode方法流程解析

    "Java重写equals及hashcode方法流程解析" Java中的equals和hashCode方法是两个非常重要的方法,它们都是Object类中的方法。在实际开发中,正确地重写这两个方法对于确保程序的正确性和性能至关重要。下面,我们将...

    Java理论与实践:hashCode()和equals()方法

    本文介绍了Java语言不直接支持关联数组,可以使用任何对象作为一个索引的数组,但在根Object类中使用 hashCode()方法明确表示期望广泛使用HashMap。理想情况下基于散列的容器提供有效插入和有效检索;直接在对象模式...

    Java中的equals和hashCode方法详解1

    在Java编程语言中,`equals()`和`hashCode()`方法是对象的基本组成部分,它们主要用于对象的比较和存储。这两个方法在`java.lang.Object`类中定义,因此所有的Java类都默认继承了这两个方法。然而,根据具体的应用...

    Java_重写equals()和hashCode()

    在Java编程语言中,`equals()` 和 `hashCode()` 方法是对象的基本组成部分,它们在很多场景下都发挥着至关重要的作用。这两个方法与对象的相等性比较和哈希表(如HashMap、HashSet)的运作紧密相关。这篇博客将深入...

    equals-hashcode-processor-1.0.0.zip

    "retry.zip"中的开源项目"equals-hashcode-processor-1.0.0"为我们提供了一个优雅的解决方案,通过这个库,我们可以轻松地为Scala Futures实现自动重试逻辑。 首先,我们需要理解Scala Futures的基础。Futures是...

    关于重写equals,hashcode以及compareTo方法!

    关于重写equals、hashcode以及compareTo方法! equals()方法是Object类中的一个方法,它用于比较两个对象是否相等。然而,它的默认实现是比较对象的引用(地址),而不是比较对象的实际内容。因此,在某些情况下,...

    JAVA集合的使用(List、Map、Set、Queue,Collections、Comparable与Comparator,排序、搜索,内部类、equals、hashCode)

    要注意的是List,Set,Queue继承了Collection接口,...这里想用一个简单的例子展示一下他们的使用,内容包括:List、Map、Set、Queue,Collections、Comparable与Comparator,排序、搜索,内部类,泛型、重写equals、hashCode

    equals 和 hashCode两者效果分析详解.docx

    在Java编程语言中,`equals()`和`hashCode()`方法是两个非常重要的概念,尤其是在处理对象比较和容器(如HashMap和HashSet)操作时。这两个方法在Java的类库中有着核心地位,尤其是对于类实例的比较和存储。接下来,...

    hashcode和equals方法

    equals()和hashcode()这两个方法都是从object类中继承过来的。当String 、Math、还有Integer、Double。。。。等这些封装类在使用equals()方法时,已经覆盖了object类的equals()方法.

    关于Object中equals方法和hashCode方法判断的分析

    在 Java 中,Object 类提供了两个重要的方法:equals 方法和 hashCode 方法。这两个方法都是用于比较两个对象是否相等的,但它们的实现机理和作用域却有所不同。 equals 方法是用于比较两个对象是否相同的。它的...

    java 基础之(equals hashcode)

    在Java编程语言中,`equals()` 和 `hashCode()` 方法是两个非常重要的概念,尤其是在处理对象比较和哈希表(如 `HashMap` 和 `HashSet`)时。`equals()` 方法用于判断两个对象是否相等,而 `hashCode()` 方法则用于...

    ordinary-zhang#java#equals和hashcode方法详解1

    1.概述 2.为什么重写equels方法要重写hashcode方法 3.例子

Global site tag (gtag.js) - Google Analytics