Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
name.didier.david.test4j.assertions.EqualsVerifier Maven / Gradle / Ivy
package name.didier.david.test4j.assertions;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static com.google.common.collect.Lists.newArrayList;
import static name.didier.david.check4j.ConciseCheckers.checkNotNull;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Asserts that a class correctly implements {@link Object#equals(Object)} and {@link Object#hashCode()}.
*
*
* See Effective Java, Second Edition by Joshua Bloch:
*
*
*
* Item 8: Obey the general contract when overriding equals
* Item 9: Always override hashCode when you override equals
*
*
*
* Usage:
*
*
*
* class MyObject {
*
* public MyObject(int a, int b) {
* super();
* this.a = a;
* this.b = b;
* }
*
* // use a and b for equals()
* // use a and b for hashCode()
* }
*
* EqualsVerifier myObject = new EqualsVerifier(new MyObject(1, 2));
* myObject.mustBeEqualsTo(new MyObject(1, 2));
* myObject.mustNotBeEqualsTo(new Object());
* myObject.mustNotBeEqualsTo(new MyObject(9, 2));
* myObject.mustNotBeEqualsTo(new MyObject(1, 9));
* myObject.mustNotBeEqualsTo(new MyObject(9, 9));
* myObject.verify();
*
*
* @author ddidier
*/
public class EqualsVerifier {
/** The associated logger. */
private static final Logger logger = LoggerFactory.getLogger(EqualsVerifier.class);
/** The tested object. */
private final Object object;
/** Objects that are known to be equals to {@code #object}. */
private final List equalsObjects = newArrayList();
/** Objects that are known to not be equals to {@code #object}. */
private final List unequalsObjects = newArrayList();
/**
* Default constructor.
*
* @param object the object to be verified.
*/
public EqualsVerifier(final Object object) {
super();
this.object = checkNotNull(object, "object");
}
/**
* Adds objects that are known to be equals to {@code #object} to test against.
*
* @param equalObject an object that is known to be equals to {@code #object}.
* @param equalObjects objects that are known to be equals to {@code #object}.
* @return {@code this} instance.
*/
public EqualsVerifier mustBeEqualsTo(final Object equalObject, final Object... equalObjects) {
equalsObjects.add(equalObject);
equalsObjects.addAll(asList(equalObjects));
for (Object eo : equalsObjects) {
checkNotNull(eo, "equalObject");
}
return this;
}
/**
* Adds objects that are known to not be equals to {@code #object} to test against.
*
* @param unequalObject an object that is known to not be equals to {@code #object}.
* @param unequalObjects objects that are known to not be equals to {@code #object}.
* @return {@code this} instance.
*/
public EqualsVerifier mustNotBeEqualsTo(final Object unequalObject, final Object... unequalObjects) {
unequalsObjects.add(unequalObject);
unequalsObjects.addAll(asList(unequalObjects));
for (Object ue : unequalsObjects) {
checkNotNull(ue, "unequalObject");
}
return this;
}
/**
* Asserts that {@code #object} correctly implements {@link Object#equals(Object)} and {@link Object#hashCode()}
* using provided {@code #equalsObjects} and {@code #unequalsObjects}.
*/
public void verify() {
verifyEquals();
verifyNotEquals();
}
/**
* Asserts that {@code #object} correctly implements {@link Object#equals(Object)} and {@link Object#hashCode()}
* using provided {@code #equalsObjects}.
*/
private void verifyEquals() {
// reflexive: for any non-null reference value {@code x}, {@code x.equals(x)} should return {@code true}.
assertEquals(object, object);
// symmetric: for any non-null reference values {@code x} and {@code y}, {@code x.equals(y)} should
// return {@code true} if and only if {@code y.equals(x)} returns {@code true}.
if (equalsObjects.isEmpty()) {
logger.warn("There is no equals object to test against, use #mustBeEqualsTo()");
} else {
// transitive:
// - equals: for any non-null reference values {@code x}, {@code y}, and {@code z}, if {@code x.equals(y)}
// returns {@code true} and {@code y.equals(z)} returns {@code true}, then {@code x.equals(z)} should return
// {@code true}.
// - hashCode: if two objects are equal according to the {@code equals(Object)} method, then calling the
// {@code hashCode} method on each of the two objects must produce the same integer result.
List objects = newArrayList();
objects.add(object);
objects.addAll(equalsObjects);
for (Object object1 : objects) {
for (Object object2 : objects) {
// consistent: for any non-null reference values {@code x} and {@code y}, multiple invocations of
// {@code x.equals(y)} consistently return {@code true} or consistently return {@code false},
// provided no information used in {@code equals} comparisons on the objects is modified.
assertEquals(object1, object2);
assertEquals(object1, object2);
assertEquals(object1, object2);
}
}
}
}
/**
* Asserts that {@code #object} correctly implements {@link Object#equals(Object)} and {@link Object#hashCode()}
* using provided {@code #unequalsObjects}.
*/
private void verifyNotEquals() {
// for any non-null reference value {@code x}, {@code x.equals(null)} should return {@code false}.
assertNotEquals(object, null);
if (unequalsObjects.isEmpty()) {
logger.warn("There is no unequals object to test against, use #mustNotBeEqualsTo()");
} else {
// transitive:
// - hashCode: it is NOT required that if two objects are unequal according to the
// {@link java.lang.Object#equals(java.lang.Object)} method, then calling the {@code hashCode} method on
// each of the two objects must produce distinct integer results. However, the programmer should be aware
// that producing distinct integer results for unequal objects may improve the performance of hash tables.
for (Object unequalsObject : unequalsObjects) {
// consistent: for any non-null reference values {@code x} and {@code y}, multiple invocations of
// {@code x.equals(y)} consistently return {@code true} or consistently return {@code false},
// provided no information used in {@code equals} comparisons on the objects is modified.
assertNotEquals(object, unequalsObject);
assertNotEquals(object, unequalsObject);
assertNotEquals(object, unequalsObject);
}
}
}
/**
* Asserts that the actual object is equals to the expected one, and the opposite if expected is not {@code null}.
* Asserts that the hashcode of the actual object is equals to the hashcode of the expected one.
*
* @param actual the actual object to test the expected object against.
* @param expected the expected object to test the actual object against.
*/
private static void assertEquals(final Object actual, final Object expected) {
// for code coverage, because AssertJ bypasses some things.
assertThat(actual.equals(expected)).isTrue();
// symmetric
if (expected != null) {
assertThat(expected.equals(actual)).isTrue();
}
if (expected != null) {
assertThat(actual.hashCode()).isEqualTo(expected.hashCode());
// whenever it is invoked on the same object more than once during an execution of a Java application, the
// {@code hashCode} method must consistently return the same integer, provided no information used in
// {@code equals} comparisons on the object is modified. This integer need not remain consistent from one
// execution of an application to another execution of the same application.
int hashCode = actual.hashCode();
assertThat(actual.hashCode()).isEqualTo(hashCode);
assertThat(actual.hashCode()).isEqualTo(hashCode);
assertThat(actual.hashCode()).isEqualTo(hashCode);
}
}
/**
* Asserts that the actual object is not equals to the expected one, and the opposite if expected is not
* {@code null}.
*
* @param actual the actual object to test the expected object against.
* @param expected the expected object to test the actual object against.
*/
private static void assertNotEquals(final Object actual, final Object expected) {
// for code coverage, because AssertJ bypasses some things.
assertThat(actual.equals(expected)).isFalse();
// symmetric
if (expected != null) {
assertThat(expected.equals(actual)).isFalse();
}
// optional but good practice
if (expected != null) {
assertThat(actual.hashCode()).isNotEqualTo(expected.hashCode());
}
}
}