Java 一个单元应该如何测试hashCode=contract?
简而言之,根据Java的object.hashCode(),hashCode契约:Java 一个单元应该如何测试hashCode=contract?,java,unit-testing,oop,Java,Unit Testing,Oop,简而言之,根据Java的object.hashCode(),hashCode契约: 除非影响equals()的内容发生更改,否则哈希代码不应更改 equals()表示散列码是== 让我们假设主要对不可变数据对象感兴趣——它们的信息在构造之后永远不会改变,因此假定#1是有效的。剩下的是#2:问题只是确认equals意味着hash code== 显然,我们无法测试所有可能的数据对象,除非该集合非常小。那么,编写可能捕获常见情况的单元测试的最佳方法是什么 因为这个类的实例是不可变的,所以构造这样一个对
[这可能适用于其他OO语言,因此我正在添加该标记。]我的建议是思考为什么/如何可能不成立,然后编写一些针对这些情况的单元测试 例如,假设您有一个自定义的
集合
类。如果两个集合包含相同的元素,则两个集合是相等的,但是如果以不同的顺序存储这些元素,则两个相等集合的底层数据结构可能会不同。例如:
MySet s1 = new MySet( new String[]{"Hello", "World"} );
MySet s2 = new MySet( new String[]{"World", "Hello"} );
assertEquals(s1, s2);
assertTrue( s1.hashCode()==s2.hashCode() );
在这种情况下,集合中元素的顺序可能会影响它们的散列,这取决于您实现的散列算法。这就是我要写的测试,因为它测试的情况是,我知道对于我定义为相等的两个对象,一些散列算法可能会产生不同的结果
您应该对自己的自定义类使用类似的标准,不管是什么。这是我在测试中拥有多个断言的唯一情况之一。由于需要测试equals方法,因此还应该同时检查hashCode方法。因此,在每个equals方法测试用例上,也要检查hashCode契约
A one = new A(...);
A two = new A(...);
assertEquals("These should be equal", one, two);
int oneCode = one.hashCode();
assertEquals("HashCodes should be equal", oneCode, two.hashCode());
assertEquals("HashCode should not change", oneCode, one.hashCode());
当然,检查一个好的哈希代码是另一个练习。老实说,我不会费心去做双重检查,以确保hashCode在同一次运行中没有发生更改,通过在代码审查中捕获它并帮助开发人员理解为什么这不是编写hashCode方法的好方法,可以更好地处理这类问题。我建议使用GSBase提供的方法。它基本上是你想要的。但我有两个(小)问题:
- 构造函数做所有的工作,我认为这不是很好的实践。
- 当类A的一个实例等于类A的一个子类的一个实例时,它将失败。这不一定违反equals契约
hashCode
和equals
彼此一致。我解决这个问题的方法是想象程序员在编写有问题的类时所采用的公共路径,即不可变数据。例如:
MySet s1 = new MySet( new String[]{"Hello", "World"} );
MySet s2 = new MySet( new String[]{"World", "Hello"} );
assertEquals(s1, s2);
assertTrue( s1.hashCode()==s2.hashCode() );
equals()
而不写hashCode()
。这通常意味着相等被定义为两个实例的字段相等hashCode()
而不写equals()
。这可能意味着程序员正在寻找一种更有效的散列算法equals()
,因此不需要其他实例具有相等的哈希代码。在最坏的情况下,哈希算法可能会导致哈希映射的性能较差,这超出了本问题的范围
在#1的情况下,标准单元测试需要使用传递给构造函数的相同数据创建同一对象的两个实例,并验证相等的哈希代码。假阳性呢?在一个不健全的算法上,可以选择恰好产生相同哈希代码的构造函数参数。一个倾向于避免这些参数的单元测试将满足这个问题的精神。这里的捷径是检查equals()
的源代码,认真思考,并在此基础上编写一个测试,但尽管在某些情况下这可能是必要的,但也可能存在捕获常见问题的常见测试,这些测试也满足了这个问题的精神
例如,如果要测试的类(称为Data)有一个接受字符串的构造函数,并且由equals()
字符串构造的实例生成了equals()
的实例,那么好的测试可能会测试:
新数据(“foo”)
- 另一个
新数据(“foo”)
新数据(new String(“foo”)
)的哈希代码,以强制该字符串不被占用,尽管这比数据更可能产生正确的哈希代码。在我看来,equals()
会产生正确的结果
Eli Courtwright的回答是一个例子,他仔细考虑了一种打破基于
equals
规范的哈希算法的方法。一个特殊集合的例子是一个很好的例子,因为用户制作的collection
s有时会出现,并且在散列算法中很容易出错。值得为此使用junit插件。检查类EqualsHashCodeTestCase您可以扩展它并实现createInstance和CreateNoteQualifInstance,这将检查equals和hashCode方法是否正确。是相对论
public static void checkInvariants(final Thing thing1, Thing thing2) {
ObjectTest.checkInvariants(thing1, thing2);
... invariants that are specific to Thing
}