Automagic unit tests for upholding Object method c

2020-03-01 17:20发布

When developing Java applications, I often override Object methods (usually equals and hashCode). I would like some way to systematically check that I'm adhering to the contract for Object methods for every one of my classes. For example, I want tests that assert that for equal objects, the hash code is also equal. I'm using the JUnit test framework, so preferably I'd like some JUnit solution where I can automatically generate these tests, or some test case that can somehow visit all of my classes and make sure that the contract is upheld.

I'm using JDK6 and JUnit 4.4.

8条回答
劫难
2楼-- · 2020-03-01 17:36
    public static void checkObjectIdentity(Object a1, Object a2, Object b1) {
        assertEquals(a1, a2);
        assertEquals(a2, a1);
        assertNotSame(a1, a2);
        assertEquals(a1.hashCode(), a2.hashCode());
        assertFalse(a1.equals(b1));
        assertFalse(a2.equals(b1));
        assertFalse(b1.equals(a1));
        assertFalse(b1.equals(a2));
    }

Usage:

        checkObjectIdentity(new Integer(3), new Integer(3), new Integer(4));

Can't think of anything better. Add new calls to checkObjectIdentity when you find a bug.

查看更多
贪生不怕死
3楼-- · 2020-03-01 17:40

This problem doesn't have "easy" solution unless you're putting strong constraints on your classes.

For example, if you're using several constructors for a given class, how can you ensure that all your parameters are well taken into account in your equals/hash methods? How about defaults values? These are things that, unfortunately, cannot be automated blindly.

查看更多
▲ chillily
4楼-- · 2020-03-01 17:46

Maybe I'm misunderstanding the question (and being too CS), but it doesn't sound like the problem you're describing is decidable in the general case.

In other words, the only way a unit test can assure you that an overriding method works the same on all inputs as the overridden method would be to try it on all the inputs; in the case of equals, that would mean all object states.

I am not sure if any current test framework will automatically trim down and abstract the possibilities for you.

查看更多
一夜七次
5楼-- · 2020-03-01 17:50

I think VonC's on the right track, but I would even settle for something less sophisticated, such as a parameterized test that takes in the .class object (for which the Object methods are being tested), followed by a variable number of constructor args. Then, you'd have to use reflection to find the constructor that matches the types for the passed-in arguments, and call the constructor. This test would assume that the parameters being passed into it would create a valid instance of the object.

The downside to this solution is that you have to "register" each class you want to test with this test class, and you have to make sure that valid input is given to the constructor, which would not always be easy. In that light, I'm on the fence as to whether or not this would be more or less work than manually writing all the tests for each class anyway.

Vote up if you think this could work...leave a comment if you want me to flush it out more (if it turns out to be a feasible solution, I may just do this anyway)

查看更多
ら.Afraid
6楼-- · 2020-03-01 17:50

New answer for an old question, but in May of 2011 Guava (formerly Google Collections) released a class that removes a lot of the boilerplate, called EqualsTester. You still have to create your own instances but it takes care of comparing each object to itself, to null, to every object in the equality group, to every object in every other equality group, and to a secret instance that should match nothing. It also checks that a.equals(b) implies a.hashCode() == b.hashCode() across all those combinations.

Example from Javadoc:

new EqualsTester()
   .addEqualityGroup("hello", "h" + "ello")
   .addEqualityGroup("world", "wor" + "ld")
   .addEqualityGroup(2, 1 + 1)
   .testEquals();
查看更多
7楼-- · 2020-03-01 17:58

I have a first rough implementation, for equals testing with Constructor using only primitive parameters here. Just copy-paste it in test.MyClass.java file and run it.

Warning: 1720 lines of code (0 errors in findbugs, 0 in "modified" checkstyle, cyclomatic complexity under 10 for all functions).

See all the code at: Auto-test for equals function in java classes through annotations

查看更多
登录 后发表回答