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 18:00

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)
查看更多
闹够了就滚
3楼-- · 2020-03-01 18:01

[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 :)

查看更多
登录 后发表回答