11

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.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
Julie
  • 6,221
  • 3
  • 31
  • 37
  • What version of junit are you using ? And, as important, in what version of jdk are you coding ? In other words, are annotations available (jdk1.5 or more) or not (jdk1.4) ? – VonC Oct 10 '08 at 03:54
  • Good catch! I edited my question to include that info. :-) – Julie Oct 10 '08 at 03:57
  • code-challenge taken and coded (first version though, can be improved) – VonC Oct 19 '08 at 15:09
  • maybe this will help: http://stackoverflow.com/questions/125100/free-open-source-test-generator-for-java – yegor256 Oct 31 '12 at 10:26
  • This is a great idea! The [Quick Check](https://bitbucket.org/blob79/quickcheck) specification based testing framework is based on similar ideas. Maybe that can be used for this, or serve as a source for inspiration? – Lii Mar 24 '15 at 13:02

8 Answers8

2
    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.

alex
  • 5,213
  • 1
  • 24
  • 33
1

Just a some initial thoughts on that question (which may explain why there are still no answer after a full hour!? ;)

There seems to be two parts when it comes to implement a solution to the question:

1/ retrieve every classes of my own. Easy, you give a jar name, the Junit test initialization method would:

  • check if that jar is in the JUnit execution classpath
  • read and load every classes in it
  • memorizes only those for which equals() and hash() has been declared and redefined (through Reflection)

2/ test every objects
... and therein lies the catch: you have to instantiate those objects, that is create two instances, and use them for equals() tests.

That means if your constructors are taken arguments, you have to consider,

  • for primitive types arguments (int, boolean, float, ...) or String, every combinations of limit values (for a String, "xxx", "", null; fonr int, 0, -x, +x, -Integer.MIN, +Integer.MAX, ... and so on)
  • for non-primitive types, build an instance of those to be passed to the constructor of the object to test (meaning you recursively have to consider the constructor parameters of that parameter: primitive types or not)

Finally, not every parameters automatically created for those constructor would make sense in a functional way, meaning some of those values will fail to build the instance because of an Assert: that must be detected.

Yet it seems to be possible (you can make it a code-challenge if you want), but I want first let other StackOverflow readers respond to this issue, as they may see a far simpler solution that I am.


To avoid combinations problem and to keep test relevant testing values close to the actual code itself, I would recommend the definition of an dedicated annotation, with a String representing valid values for constructors. There would be located right above the equals() overridden method of one of your object.

Those annotation values would then be read, and the instances created from those would be combined for testing equals(). That would keep the number of combinations down enough

Side-node: a generic JUnit test case would of course check that, for each equals() to tests, there is:

  • some annotations as described above (unless there is only default constructor available)
  • a corresponding hash() method also overridden (if not, if would throw an assert exception and fail on that class)
Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Interesting thought. I was afraid it might be complicated. Perhaps a parameterized test would work, where each invocation takes in the .class object, followed by constructor args. Perhaps I'll post this as a possible answer. – Julie Oct 10 '08 at 04:24
0

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();
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
0

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)

Julie
  • 6,221
  • 3
  • 31
  • 37
  • those parameters should be in the code of the class to be tested itself: that would solve the "registration" issue. See my answer with more details on that possible implementation. – VonC Oct 10 '08 at 05:49
  • Note: this is definitively a "code-challenge" kind of question. If you let me, I will add a "community answer" with some specs: build a single JUnit test-case which, by default, would test itself regarding to overridden equals and hash methods defined on itself: in short, a self-contained test. – VonC Oct 10 '08 at 05:51
  • I love the annotations idea. I'm not sure what a "community answer" is, but this is the best way to find out, so go for it! – Julie Oct 10 '08 at 14:43
  • Right on! Now that is the spirit ;) Another code-challenge is on! – VonC Oct 10 '08 at 14:56
  • code-challenge taken and coded (first version though, can be improved) – VonC Oct 19 '08 at 15:10
0

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.

gizmo
  • 11,819
  • 6
  • 44
  • 61
  • Regarding to equals(), you need to define within the class overriding it the correct values to test. Those values, you are right, will not be automated or guessed. But the challenge can be on the testing mechanism itself. Now that can be automated! – VonC Oct 10 '08 at 08:30
0

[community post here, no karma involved ;) ]

Here is another code-challenge for you:

One java class, implementing a JUnit test case, with a main method able to launch JUnit on itself!

This class will also:

  • override hash() and equals()
  • define a few attributes (with primitive types)
  • define a default constructor but also some constructors with various combinations of parameters
  • define an annotation able to enumerate "interesting" values to pass to those constructor
  • annotate equals() with those "interesting" values

The test method takes a class name parameter (here: it will be itself), check if the class with that name has an equals() overridden method with "interesting values" annotations.
If it does, it will builds the appropriate instances (of itself) based on the annotations, and test equals()

This is a self-contained test class, which defines a mechanism able to be generalized to any class with an annotated overridden equals() function.

Please Use JDK6 and JUnit4.4

That class should be copied-paste in the appropriate package of an empty java project... and just run ;)


To add some more thought, in response to Nicolas (see comments):

  • yes the data needed for test are within the class candidate to be tested (that is, the one overriding equals and helping any 'automatic tester' to build appropriate instances)
  • I do not see that exactly as "testing logic", but as useful comments on what is supposed to do the equals (and incidentally as data to be exploited by the aforementioned tester ;) )

Should annotations representing potential testing data never ever be in the class itself ?... Hey that could be a great question to ask :)

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • > define an annotation able to enumerate "interesting" values to pass to those constructor I'm not sure to understand... The annotation adds testing logic inside the class to test ? If it's okay, it's... kinda ugly. – Nicolas Oct 10 '08 at 15:08
  • Where do I go to see answers to this challenge? I'm new to the "community wiki" thing... – Julie Oct 10 '08 at 15:18
  • @Julie the answers will be posted in this very page, as they would be valid answer to your question. – VonC Oct 10 '08 at 15:26
  • @Nicolas I agree but I in this case, that may be useful for the kind of automatic test Julie is after. – VonC Oct 10 '08 at 15:29
0

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.

Uri
  • 88,451
  • 51
  • 221
  • 321
  • That's not really what I'm after. With most unit tests, it's only possible to test with a few different values. I'm looking looking for unit tests that will use a representative set of carefully chosen constructor arguments. – Julie Oct 10 '08 at 16:00
0

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

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Thanks so much for taking a first stab at this! I have it on my todo list to test this out - can't wait to play with it. – Julie Oct 20 '08 at 17:45
  • You're welcome. For now, it only allow testing for object with constructor using *primitive* arguments. If you are convinced by this approach, I will extend it to include constructor with non-primitive parameters. – VonC Oct 20 '08 at 18:06