Java library for verifying the contract of equals and hashCode methods in unit tests
—
Batch verification capabilities for testing multiple classes with shared configuration. The MultipleTypeEqualsVerifierApi supports package scanning, filtering, and exception handling for large codebases.
Creates a verifier for testing multiple classes from an iterable collection.
/**
* Factory method for general use
* @param classes An iterable containing the classes for which equals method should be tested
* @return A fluent API for EqualsVerifier
*/
public static MultipleTypeEqualsVerifierApi forClasses(Iterable<Class<?>> classes);Usage Examples:
import nl.jqno.equalsverifier.EqualsVerifier;
import java.util.Arrays;
// From a list of classes
List<Class<?>> classes = Arrays.asList(Person.class, Address.class, Phone.class);
EqualsVerifier.forClasses(classes)
.suppress(Warning.STRICT_INHERITANCE)
.verify();Creates a verifier for testing multiple classes using varargs syntax.
/**
* Factory method for general use
* @param first A class for which the equals method should be tested
* @param second Another class for which the equals method should be tested
* @param more More classes for which the equals method should be tested
* @return A fluent API for EqualsVerifier
*/
public static MultipleTypeEqualsVerifierApi forClasses(
Class<?> first,
Class<?> second,
Class<?>... more
);Usage Examples:
// Direct class specification
EqualsVerifier.forClasses(Person.class, Address.class, Phone.class)
.suppress(Warning.STRICT_INHERITANCE)
.verify();
// With configuration
EqualsVerifier.forClasses(Person.class, Address.class)
.withPrefabValues(ExternalDependency.class, redValue, blueValue)
.verify();Scans packages to automatically discover and test classes with equals implementations.
/**
* Factory method for package scanning (non-recursive)
* Note that this operation may be slow. If the test is too slow, use forClasses instead
* @param packageName A package for which each class's equals should be tested
* @return A fluent API for EqualsVerifier
*/
public static MultipleTypeEqualsVerifierApi forPackage(String packageName);
/**
* Factory method for package scanning with recursion control
* Note that this operation may be slow. If the test is too slow, use forClasses instead
* @param packageName A package for which each class's equals should be tested
* @param scanRecursively true to scan all sub-packages
* @return A fluent API for EqualsVerifier
*/
public static MultipleTypeEqualsVerifierApi forPackage(
String packageName,
boolean scanRecursively
);
/**
* Factory method for package scanning with type filtering
* Note that this operation may be slow. If the test is too slow, use forClasses instead
* Also note that if mustExtend is given, and it exists within packageName, it will NOT be included
* @param packageName A package for which each class's equals should be tested
* @param mustExtend if not null, returns only classes that extend or implement this class
* @return A fluent API for EqualsVerifier
*/
public static MultipleTypeEqualsVerifierApi forPackage(
String packageName,
Class<?> mustExtend
);Usage Examples:
// Scan single package (non-recursive)
EqualsVerifier.forPackage("com.example.model")
.suppress(Warning.STRICT_INHERITANCE)
.verify();
// Scan package recursively
EqualsVerifier.forPackage("com.example", true)
.suppress(Warning.STRICT_INHERITANCE)
.verify();
// Scan package for specific type hierarchy
EqualsVerifier.forPackage("com.example.entities", BaseEntity.class)
.suppress(Warning.SURROGATE_KEY)
.verify();Removes specific classes from the verification list.
/**
* Removes specific types from verification list
* @param type First type to exclude
* @param more Additional types to exclude
* @return this, for easy method chaining
*/
public MultipleTypeEqualsVerifierApi except(Class<?> type, Class<?>... more);
/**
* Removes types matching predicate from verification list
* @param exclusionPredicate Predicate to determine which types to exclude
* @return this, for easy method chaining
*/
public MultipleTypeEqualsVerifierApi except(Predicate<Class<?>> exclusionPredicate);Usage Examples:
// Exclude specific classes
EqualsVerifier.forPackage("com.example.model")
.except(BaseClass.class, AbstractClass.class)
.verify();
// Exclude classes matching criteria
EqualsVerifier.forPackage("com.example.model")
.except(clazz -> clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers()))
.verify();
// Exclude by name pattern
EqualsVerifier.forPackage("com.example.model")
.except(clazz -> clazz.getSimpleName().endsWith("Test"))
.verify();All configuration methods from the base EqualsVerifierApi are available for batch operations.
/**
* Suppresses warnings for all classes in the batch
* @param warnings A list of warnings to suppress
* @return this, for easy method chaining
*/
public MultipleTypeEqualsVerifierApi suppress(Warning... warnings);
/**
* Adds prefabricated values for all classes in the batch
* @param otherType The class of the prefabricated values
* @param red An instance of S
* @param blue Another instance of S, not equal to red
* @return this, for easy method chaining
*/
public <S> MultipleTypeEqualsVerifierApi withPrefabValues(
Class<S> otherType,
S red,
S blue
);
/**
* Adds a factory for generic types with 1 parameter for all classes in the batch
* @param otherType The class of the prefabricated values
* @param factory A factory to generate instances
* @return this, for easy method chaining
*/
public <S> MultipleTypeEqualsVerifierApi withGenericPrefabValues(
Class<S> otherType,
Func1<?, S> factory
);
/**
* Adds a factory for generic types with 2 parameters for all classes in the batch
* @param otherType The class of the prefabricated values
* @param factory A factory to generate instances
* @return this, for easy method chaining
*/
public <S> MultipleTypeEqualsVerifierApi withGenericPrefabValues(
Class<S> otherType,
Func2<?, ?, S> factory
);
/**
* Signals that getClass is used in equals implementations for all classes
* @return this, for easy method chaining
*/
public MultipleTypeEqualsVerifierApi usingGetClass();
/**
* Sets field name to getter name converter for all classes
* @param converter A function that converts from field name to getter name
* @return this, for easy method chaining
*/
public MultipleTypeEqualsVerifierApi withFieldnameToGetterConverter(
Function<String, String> converter
);Usage Examples:
// Batch configuration
EqualsVerifier.forClasses(Person.class, Address.class, Phone.class)
.suppress(Warning.STRICT_INHERITANCE, Warning.NONFINAL_FIELDS)
.withPrefabValues(ExternalDependency.class, redValue, blueValue)
.withGenericPrefabValues(Optional.class, Optional::of)
.usingGetClass()
.verify();Methods to execute batch verification and get results.
/**
* Performs verification on all types and throws AssertionError on any failure
*/
public void verify();
/**
* Performs verification on all types and returns list of reports
* @return List of EqualsVerifierReport, one for each tested class
*/
public List<EqualsVerifierReport> report();Usage Examples:
// Basic batch verification (throws on any failure)
EqualsVerifier.forClasses(Person.class, Address.class, Phone.class)
.suppress(Warning.STRICT_INHERITANCE)
.verify();
// Report-based batch verification
List<EqualsVerifierReport> reports = EqualsVerifier
.forClasses(Person.class, Address.class, Phone.class)
.suppress(Warning.STRICT_INHERITANCE)
.report();
// Process individual results
for (EqualsVerifierReport report : reports) {
if (!report.isSuccessful()) {
System.err.println("Failed for " + report.getType().getSimpleName() +
": " + report.getMessage());
} else {
System.out.println("Passed for " + report.getType().getSimpleName());
}
}
// Count failures
long failureCount = reports.stream()
.filter(report -> !report.isSuccessful())
.count();Configuration Reuse with Multiple Classes:
// Create base configuration
ConfiguredEqualsVerifier config = EqualsVerifier.configure()
.suppress(Warning.STRICT_INHERITANCE, Warning.NONFINAL_FIELDS)
.withPrefabValues(ExternalDependency.class, redValue, blueValue);
// Apply to different class sets
config.forClasses(Person.class, Address.class).verify();
config.forPackage("com.example.entities").verify();Package Scanning with Complex Filtering:
// Scan packages with sophisticated filtering
EqualsVerifier.forPackage("com.example.model", true)
.except(clazz -> {
// Exclude test classes
if (clazz.getSimpleName().endsWith("Test")) return true;
// Exclude abstract classes and interfaces
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) return true;
// Exclude classes without equals override
try {
clazz.getDeclaredMethod("equals", Object.class);
return false; // Has equals method, include it
} catch (NoSuchMethodException e) {
return true; // No equals method, exclude it
}
})
.suppress(Warning.INHERITED_DIRECTLY_FROM_OBJECT)
.verify();Conditional Verification:
// Verify different class sets based on environment
List<Class<?>> entityClasses = Arrays.asList(User.class, Order.class, Product.class);
MultipleTypeEqualsVerifierApi verifier = EqualsVerifier.forClasses(entityClasses)
.suppress(Warning.STRICT_INHERITANCE);
if (isJpaEnvironment()) {
verifier.suppress(Warning.SURROGATE_KEY);
}
verifier.verify();Install with Tessl CLI
npx tessl i tessl/maven-nl-jqno-equalsverifier--equalsverifier