How does one unit test hashCode-equals contract?

In a nutshell, the hashCode contract, according to Java object.hashCode ():

  • The hash code should not be changed unless something affects the equals () values
  • equals () means hash codes == == /

Suppose that interest primarily relates to immutable data objects - their information never changes after they are built, so it is assumed that # 1 is preserved. This leaves # 2: the problem is only to confirm that equals means the hash code ==.

Obviously, we cannot test every conceivable data object, unless this set is trivially small. So what is the best way to write unit test, which is likely to catch common cases?

Since instances of this class are immutable, there are limited ways to construct such an object; this unit test should cover everything if possible. At the top of my head, entry points are the constructors, deserialization, and subclass constructors (which should be brought to the issue of calling the constructor).

[I will try to answer my question through research. Logging in from other StackOverflowers is a reliable security mechanism for this process.]

[This may apply to other OO languages, so I am adding this tag.]

+60
java oop unit-testing
Oct 09 '08 at 17:29
source share
8 answers

EqualsVerifier is a relatively new open source project, and it works very well when testing an equal contract. He has no questions that GSBase has EqualsTester. I would definitely recommend this.

+57
Sep 11 '09 at 7:36
source share

My advice would be to think about why / how it could ever be invalid, and then write some unit tests designed for these situations.

For example, let's say you had a custom Set class. Two sets are equal if they contain the same elements, but it is possible that the basic data structures of two equal sets will be different if these elements are stored in a different order. For example:

 MySet s1 = new MySet( new String[]{"Hello", "World"} ); MySet s2 = new MySet( new String[]{"World", "Hello"} ); assertEquals(s1, s2); assertTrue( s1.hashCode()==s2.hashCode() ); 

In this case, the order of the elements in the sets may affect their hash, depending on the hashing algorithm that you implemented. So these are the tests that I would write, since it checks the case when I know that for some hashing algorithm it will be possible to get different results for two objects, which I defined as equal.

You should use a similar standard with your own custom class, whatever that is.

+9
Oct 09 '08 at 17:38
source share

To do this, use junit addons. Check the EqualsHashCodeTestCase class http://junit-addons.sourceforge.net/ , you can extend it and implement createInstance and createNotEqualInstance, this will check the correctness of the equals and hashCode methods.

+6
Nov 10 '08 at 17:02
source share

I would recommend EqualsTester from GSBase. This is basically what you want. I have two (minor) issues:

  • The designer does all the work that I do not consider good practice.
  • This fails if the instance of class A is equal to the instance of a subclass of class A. This is not necessarily a violation of an equal contract.
+4
Oct 09 '08 at 18:58
source share

[At the time of this writing, three other answers were published.]

I repeat, the goal of my question is to find standard test cases to confirm that hashCode and equals agree with each other. My approach to this issue is to present the general paths used by programmers to write the classes in question, namely, immutable data. For example:

  • Wrote equals() without writing hashCode() . This often means that equality has been defined as meaning the equality of the fields of two copies.
  • Wrote hashCode() without writing equals() . This may mean that the programmer was looking for a more efficient hash algorithm.

In case # 2, the problem seems insignificant to me. No additional instances were made by equals() , so no extra instances are required to obtain equal hash codes. In the worst case, a hashing algorithm may produce worse performance for hash cards, which is beyond the scope of this question.

In case # 1, the standard unit test entails creating two instances of the same object with the same data passed to the constructor, and checking for equal hash codes. What about false positives? You can select constructor parameters that simply result in equal hash codes on a nonetheless unfounded algorithm. A unit test, which seeks to avoid such parameters, will be in keeping with the spirit of this question. The shortcut here is to check the source code for equals() , ponder and write a test based on this, but in some cases it may be necessary, there may also be general tests that catch common problems - and such tests also fulfill the spirit of this question .

For example, if the test class (call it Data) has a constructor that takes a string, and instances built from strings that equals() get instances that were equals() , then a good test will probably check

  • new Data("foo")
  • another new Data("foo")

We could even check the hash code for new Data(new String("foo")) to make String not be interned, although more likely to get the correct hash code than Data.equals() should give the correct result. in my opinion.

Eli Courtwright's answer is an example of how difficult it is to overcome a hash algorithm based on knowledge of the equals specification. An example of a special collection is a good one, as custom Collection sometimes appear and are very prone to errors in the hash algorithm.

+2
Oct 10 '08 at
source share

This is one of the only cases where I would have several statements in the test. Since you need to check the equals method, you must also check the hashCode method at the same time. This way, the hashCode contract is also checked on each of your test cases for the equals method.

 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()); 

And of course, checking for a good hash code is another exercise. Honestly, I would not do double checking to make sure that hashCode does not change in the same run, this problem is better handled by catching it in the code review and helping the developer understand why it is not very good to write hashCode methods.

+1
Oct 09 '08 at 18:53
source share
+1
Jul 21 '13 at 17:37
source share

If I have a Thing class, like most others, I write a ThingTest class that contains all the unit tests for this class. Each ThingTest has a method

  public static void checkInvariants(final Thing thing) { ... } 

and if the Thing class overrides hashCode and is equal to it has a method

  public static void checkInvariants(final Thing thing1, Thing thing2) { ObjectTest.checkInvariants(thing1, thing2); ... invariants that are specific to Thing } 

This method is responsible for checking all invariants that are designed to be stored between any pair of Thing objects. The ObjectTest method to which he delegates is responsible for checking all invariants that must be held between any pairs of objects. Since equals and hashCode are methods of all objects, this method checks hashCode and equals match.

Then I have several testing methods that create pairs of Thing objects and pass them to the paired checkInvariants method. I use equivalence splitting to decide which pairs are worth testing. Usually I create each pair to differ from only one attribute, plus a test that checks two equivalent objects.

I also sometimes have a method with three arguments to checkInvariants , although I find it less useful for detecting defects, so I often don't

0
Jul 21 '13 at 23:03
source share



All Articles