One thing, that leads to many discussions, is how and if equals and hashCode methods should be tested.
Most of the time these methods get generated.
It doesn’t seem right to test generated code.
On the other hand, not testing these methods leads to poor test coverage statistics.
In rare cases, when these methods are not regenerated or edited manually,
they might cause bugs, that are very hard to find.
In this post, I’ll show you an effective way to test all equals and hashCode methods within one single test class.
The source code for this example can be found
here.
JUnit Test Class
You first need no import the classes that should be analyzed by ArchUnit. Since it may take a while, it should only be done once per class (hence static).
EqualsVerifier can be configured if the default settings don’t fit. In this particular case, the hashCode implementation can skip some fields that are compared inside the equals method, which still meets the contract.
The exchangeable ConfiguredEqualsVerifier is used to generate the report for equals and hashCode methods. This is done for the JavaCodeUnit, that is currently selected by ArchUnit.
The report of the EqualsVerifier is converted to a SimpleConditionEvent and added to the other events. If all checks are successful, the test will pass. If one of the
CodeUnits doesn’t fulfill the contract, it will be listed in the message of the failed test.
Upgrading a test to a specification
There is a significant difference in the purpose for which the test is written for.
Finding bugs is the most obvious one.
Documenting, what the code should do and how it’s expected to behave
takes it to the next level. It can now be seen as a specification.
Have a look at behaviour-driven development, if you want to find out more about it.
JUnit 5 provides the annotation @DisplayName for more expressive test case names. This is the most significant difference between a specification and an ordinary test. The test method name can be used as well, e.g. when using JUnit 4.
ArchUnit supports a whole bunch of ways to select classes. In this example,
entities and value objects are simply distinguished by their class name.