Why should I use EqualsVerifier?
I have worked on many different Java projects, and when it is time to unit test an
equals method, most projects fall into one of two categories. The first category has strong coverage rules, so they write several pages of tests for a single
equals method, just to achieve 100% coverage on it. The second category of projects doesn’t have the requirement. They write maybe one or two tests to check the happy path. Sometimes not even that, because why test something that’s generated by the IDE anyway?
I believe that if something is worth doing, it’s worth doing it right.
equals has a bunch of requirements written in its Javadoc, and they’re there for a reason. If you don’t get them right, you might run into weird problems that are hard to debug. For instance,
set.contains(foo) might return
false on an object that you just added to the set. WTF?
The IDE won’t save you either. You might add a field to the class and forget to regenerate
equals, for example. Or you might make a subclass and override
equals there, too. Suddenly
true, even though your IDE generated both
equals methods and therefore they should be correct. WTF?
So you want to get it right, but you don’t want to write pages upon pages of tests for a single
equals method either.
Enter EqualsVerifier. With EqualsVerifier, your test becomes a one-liner:
You get 100% coverage and the confidence that all the requirements of the
equals contract are tested. Not only that, but
hashCode as well. What’s not to love?
There is one downside to this. EqualsVerifier is an opinionated tool. It has to be. Unfortunately, that means it can feel restrictive to some people:
All right. Goodbye. EqualsVerifier. I cannot handle your needless constraints.— ☕ J. B. Rainsberger (@jbrains) November 3, 2015
This manual is an attempt to explain these constraints are necessary. It also explains how you can remove the constraints if you don’t agree with them. If you still feel EqualsVerifier is too restrictive, then that’s fine; you don’t have to use it if you don’t want. But if you do, the quality of your
equals methods will improve, and so will your test coverage.
What does EqualsVerifier do?
EqualsVerifier tests your
hashCode methods by calling them repeatedly with different values.
It checks the following properties:
- Preconditions for EqualsVerifier itself (like: did the fields specified in
- The five properties of the
- The same five properties within an inheritance hierarchy (if applicable)
hashCodeare defined in terms of the same fields
equalshas the correct signature, so it actually overrides
equalsinstead of overload it
- That the fields of the class under test are final (this is important for consistency)
- That the class under test, or its
hashCodemethods, are final (this is important for symmetry and transitivity in inheritance hierarchies)
Arrays.deepEqualsare used for array fields
Double.compareare used for
It also gives you 100% coverage on sensible implementation of
hashCode. I say ‘sensible’ because it’s always possible to fool EqualsVerifier if you really want to 😉.
How does EqualsVerifier work?
The way EqualsVerifier achieves this, is through a lot of reflection and a little bit of bytecode manipulation.
First, it creates an instance of your class, without calling the constructor, in the same way that mocking frameworks do. This gives an object where all the fields are
null. If the class isn’t final, it also generates a subclass for the class to test with. Then, EqualsVerifier invents values for all the fields, and assigns these using reflection.
Next, EqualsVerifier calls
hashCode repeatedly on various permutations of these objects to see if they return the values it expects.
Finally, it also uses reflection to look at the signature of
equals, to see if it actually overrides
equals instead of overload it.