How should one unit test the hashCode-equals contract?

前端 未结 8 1023
长发绾君心
长发绾君心 2020-12-07 13:18

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

  1. The hash code shouldn\'t change unless something affecting equals() changes
相关标签:
8条回答
  • 2020-12-07 13:21

    My advice would be to think of why/how this might ever not hold true, and then write some unit tests which target those situations.

    For example, let's say you had a custom Set class. Two sets are equal if they contain the same elements, but it's possible for the underlying data structures of two equal sets to differ if those 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 might affect their hash, depending on the hashing algorithm you've implemented. So this is the kind of test I'd write, since it tests the case where I know it would be possible for some hashing algorithm to produce different results for two objects I've defined to be equal.

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

    0 讨论(0)
  • 2020-12-07 13:28

    This is one of the only cases where I would have multiple asserts in a test. Since you need to test the equals method you should also check the hashCode method at the same time. So on each of your equals method test cases check the hashCode contract as well.

    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 hashCode is another exercise. Honestly I wouldn't bother to do the double check to make sure the hashCode wasn't changing in the same run, that sort of problem is better handled by catching it in a code review and helping the developer understand why that's not a good way to write hashCode methods.

    0 讨论(0)
  • 2020-12-07 13:30

    I would recommend the EqualsTester from GSBase. It does basically what you want. I have two (minor) problems with it though:

    • The constructor does all the work, which I don't consider to be good practice.
    • It fails when an instance of class A equals to an instance of a subclass of class A. This is not necessarily a violation of the equals contract.
    0 讨论(0)
  • 2020-12-07 13:33

    It's worth using the junit addons for this. Check out the class EqualsHashCodeTestCase http://junit-addons.sourceforge.net/ you can extend this and implement createInstance and createNotEqualInstance, this will check the equals and hashCode methods are correct.

    0 讨论(0)
  • 2020-12-07 13:34

    If I have a class Thing, as most others do I write a class ThingTest, which holds all the unit tests for that class. Each ThingTest has a method

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

    and if the Thing class overrides hashCode and equals it has a method

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

    That method is responsible for checking all invariants that are designed to hold between any pair of Thing objects. The ObjectTest method it delegates to is responsible for checking all invariants that must hold between any pair of objects. As equals and hashCode are methods of all objects, that method checks that hashCode and equals are consistent.

    I then have some test methods that create pairs of Thing objects, and pass them to the pairwise checkInvariants method. I use equivalence partitioning to decide what pairs are worth testing. I usually create each pair to be different in only one attribute, plus a test that tests two equivalent objects.

    I also sometimes have a 3 argument checkInvariants method, although I finds that is less useful in findinf defects, so I do not do this often

    0 讨论(0)
  • 2020-12-07 13:40

    EqualsVerifier is a relatively new open source project and it does a very good job at testing the equals contract. It doesn't have the issues the EqualsTester from GSBase has. I would definitely recommend it.

    0 讨论(0)
提交回复
热议问题