Fluent assertion framework for Java that provides clear, readable test assertions and informative failure messages
—
Extension mechanisms for creating custom subject types, correspondence-based comparisons, and advanced assertion patterns.
The primary mechanism for extending Truth with custom subjects.
/**
* Given a factory for some Subject class, returns a builder whose that(actual) method
* creates instances of that class.
* @param factory factory for creating custom subjects
*/
public static <S extends Subject, T> SimpleSubjectBuilder<S, T> assertAbout(Subject.Factory<S, T> factory);
/**
* Factory interface for creating Subject instances.
*/
public interface Subject.Factory<SubjectT extends Subject, ActualT> {
/**
* Creates a new Subject.
* @param metadata failure metadata for context
* @param actual the value under test
*/
SubjectT createSubject(FailureMetadata metadata, ActualT actual);
}Custom Subject Example:
// Define a custom subject for Person objects
public class PersonSubject extends Subject {
private final Person actual;
protected PersonSubject(FailureMetadata metadata, Person actual) {
super(metadata, actual);
this.actual = actual;
}
// Custom assertion methods
public void hasName(String expectedName) {
check("getName()").that(actual.getName()).isEqualTo(expectedName);
}
public void hasAge(int expectedAge) {
check("getAge()").that(actual.getAge()).isEqualTo(expectedAge);
}
public void isAdult() {
check("getAge()").that(actual.getAge()).isAtLeast(18);
}
// Factory for creating PersonSubject instances
public static Subject.Factory<PersonSubject, Person> persons() {
return PersonSubject::new;
}
}
// Usage
Person person = new Person("Alice", 25);
assertAbout(persons()).that(person).hasName("Alice");
assertAbout(persons()).that(person).hasAge(25);
assertAbout(persons()).that(person).isAdult();Advanced extension mechanism for complex custom subjects.
/**
* A generic, advanced method of extension of Truth to new types.
* @param factory factory for creating custom subject builders
*/
public static <CustomSubjectBuilderT extends CustomSubjectBuilder> CustomSubjectBuilderT assertAbout(
CustomSubjectBuilder.Factory<CustomSubjectBuilderT> factory);
/**
* Base class for advanced custom subject builders.
*/
public abstract class CustomSubjectBuilder {
/**
* Factory interface for creating CustomSubjectBuilder instances.
*/
public interface Factory<CustomSubjectBuilderT extends CustomSubjectBuilder> {
/**
* Creates a new CustomSubjectBuilder.
* @param metadata failure metadata for context
*/
CustomSubjectBuilderT createSubjectBuilder(FailureMetadata metadata);
}
}Flexible comparison mechanism for custom element matching logic.
/**
* Abstract class defining custom comparison logic between actual and expected elements.
*/
public abstract class Correspondence<A, E> {
/**
* Returns true if the actual and expected elements correspond according to this correspondence.
* @param actual the actual element
* @param expected the expected element
*/
public abstract boolean compare(A actual, E expected);
/**
* Returns a description of the difference between actual and expected elements, or null.
* @param actual the actual element
* @param expected the expected element
*/
public String formatDiff(A actual, E expected) {
return null; // Default implementation
}
}/**
* Returns a Correspondence that compares elements using the given binary predicate.
* @param predicate the binary predicate for comparison
* @param description human-readable description of the correspondence
*/
public static <A, E> Correspondence<A, E> from(BinaryPredicate<A, E> predicate, String description);
/**
* Returns a Correspondence that compares actual elements to expected elements by
* transforming the actual elements using the given function.
* @param actualTransform function to transform actual elements
* @param description human-readable description of the transformation
*/
public static <A, E> Correspondence<A, E> transforming(Function<A, E> actualTransform, String description);
/**
* Returns a Correspondence for comparing Double values with the given tolerance.
* @param tolerance the maximum allowed difference
*/
public static Correspondence<Double, Double> tolerance(double tolerance);Correspondence Examples:
import com.google.common.truth.Correspondence;
import java.util.function.Function;
// Case-insensitive string comparison
Correspondence<String, String> CASE_INSENSITIVE =
Correspondence.from(String::equalsIgnoreCase, "ignoring case");
List<String> actualNames = Arrays.asList("Alice", "BOB", "charlie");
List<String> expectedNames = Arrays.asList("alice", "bob", "Charlie");
assertThat(actualNames)
.comparingElementsUsing(CASE_INSENSITIVE)
.containsExactlyElementsIn(expectedNames);
// Transform-based comparison
Correspondence<Person, String> BY_NAME =
Correspondence.transforming(Person::getName, "by name");
List<Person> people = Arrays.asList(new Person("Alice"), new Person("Bob"));
assertThat(people)
.comparingElementsUsing(BY_NAME)
.containsExactly("Alice", "Bob");
// Numeric tolerance
Correspondence<Double, Double> TOLERANCE = Correspondence.tolerance(0.01);
List<Double> measurements = Arrays.asList(1.001, 2.002, 3.003);
List<Double> expected = Arrays.asList(1.0, 2.0, 3.0);
assertThat(measurements)
.comparingElementsUsing(TOLERANCE)
.containsExactlyElementsIn(expected);/**
* Returns a new Correspondence that formats diffs using the given formatter.
* @param formatter the formatter for creating diff descriptions
*/
public Correspondence<A, E> formattingDiffsUsing(DiffFormatter<? super A, ? super E> formatter);
/**
* Interface for formatting differences between actual and expected values.
*/
@FunctionalInterface
public interface DiffFormatter<A, E> {
/**
* Returns a description of the difference between actual and expected values.
* @param actual the actual value
* @param expected the expected value
*/
String formatDiff(A actual, E expected);
}Diff Formatting Example:
// Custom correspondence with detailed diff formatting
Correspondence<Person, Person> BY_PERSON_FIELDS =
Correspondence.from((actual, expected) ->
Objects.equals(actual.getName(), expected.getName()) &&
Objects.equals(actual.getAge(), expected.getAge()), "by name and age")
.formattingDiffsUsing((actual, expected) ->
String.format("expected: [name=%s, age=%d], but was: [name=%s, age=%d]",
expected.getName(), expected.getAge(),
actual.getName(), actual.getAge()));
List<Person> actual = Arrays.asList(new Person("Alice", 25));
List<Person> expected = Arrays.asList(new Person("Alice", 30));
// This will provide detailed diff information in the failure message
assertThat(actual)
.comparingElementsUsing(BY_PERSON_FIELDS)
.containsExactlyElementsIn(expected);/**
* Returns a new subject builder with custom failure message context.
* @param messageToPrepend message to prepend to failure messages
*/
public StandardSubjectBuilder withMessage(String messageToPrepend);
/**
* Class representing structured failure information.
*/
public final class Fact {
/**
* Creates a fact with a key and value.
* @param key the fact key
* @param value the fact value
*/
public static Fact fact(String key, Object value);
/**
* Creates a fact with only a key (no value).
* @param key the fact key
*/
public static Fact simpleFact(String key);
}Custom Subject with Rich Failure Messages:
public class PersonSubject extends Subject {
private final Person actual;
protected PersonSubject(FailureMetadata metadata, Person actual) {
super(metadata, actual);
this.actual = actual;
}
public void hasValidEmail() {
if (actual.getEmail() == null || !isValidEmail(actual.getEmail())) {
failWithActual(
fact("expected", "valid email address"),
fact("but email was", actual.getEmail()),
simpleFact("valid email format: user@domain.com")
);
}
}
private boolean isValidEmail(String email) {
return email.contains("@") && email.contains(".");
}
}Creating Domain-Specific Subjects:
// Subject for HTTP Response objects
public class HttpResponseSubject extends Subject {
private final HttpResponse actual;
protected HttpResponseSubject(FailureMetadata metadata, HttpResponse actual) {
super(metadata, actual);
this.actual = actual;
}
public void hasStatus(int expectedStatus) {
check("getStatus()").that(actual.getStatus()).isEqualTo(expectedStatus);
}
public void isSuccessful() {
int status = actual.getStatus();
if (status < 200 || status >= 300) {
failWithActual(
fact("expected", "successful status (200-299)"),
fact("but status was", status)
);
}
}
public void hasHeader(String name, String value) {
check("getHeader(" + name + ")")
.that(actual.getHeader(name))
.isEqualTo(value);
}
public StringSubject hasBodyThat() {
return check("getBody()").that(actual.getBody());
}
// Factory method
public static Subject.Factory<HttpResponseSubject, HttpResponse> httpResponses() {
return HttpResponseSubject::new;
}
}
// Usage
HttpResponse response = makeHttpRequest();
assertAbout(httpResponses()).that(response).isSuccessful();
assertAbout(httpResponses()).that(response).hasStatus(200);
assertAbout(httpResponses()).that(response).hasHeader("Content-Type", "application/json");
assertAbout(httpResponses()).that(response).hasBodyThat().contains("success");/**
* Base class for all Truth subjects providing common assertion methods.
*/
public class Subject {
/**
* Constructor for use by subclasses.
* @param metadata failure metadata containing context information
* @param actual the value under test
*/
protected Subject(FailureMetadata metadata, Object actual);
/**
* Factory interface for creating Subject instances.
*/
public interface Factory<SubjectT extends Subject, ActualT> {
SubjectT createSubject(FailureMetadata metadata, ActualT actual);
}
/**
* Creates a derived subject for chaining assertions.
* @param format format string for the derived subject description
* @param args arguments for the format string
*/
protected StandardSubjectBuilder check(String format, Object... args);
/**
* Reports a failure with the actual value and additional facts.
* @param facts additional facts to include in the failure message
*/
protected void failWithActual(Fact... facts);
}
/**
* Abstract class defining custom comparison logic between actual and expected elements.
*/
public abstract class Correspondence<A, E> {
// Methods documented above
}
/**
* Base class for advanced custom subject builders.
*/
public abstract class CustomSubjectBuilder {
/**
* Constructor for CustomSubjectBuilder.
* @param metadata failure metadata for context
*/
protected CustomSubjectBuilder(FailureMetadata metadata);
/**
* Factory interface for creating CustomSubjectBuilder instances.
*/
public interface Factory<CustomSubjectBuilderT extends CustomSubjectBuilder> {
CustomSubjectBuilderT createSubjectBuilder(FailureMetadata metadata);
}
}
/**
* Class representing structured failure information.
*/
public final class Fact {
// Methods documented above
}
/**
* Functional interface for binary predicates used in Correspondence.from().
*/
@FunctionalInterface
public interface BinaryPredicate<A, E> {
/**
* Applies this predicate to the given arguments.
* @param actual the actual value
* @param expected the expected value
*/
boolean apply(A actual, E expected);
}
/**
* Interface for formatting differences between actual and expected values.
*/
@FunctionalInterface
public interface DiffFormatter<A, E> {
/**
* Returns a description of the difference between actual and expected values.
* @param actual the actual value
* @param expected the expected value
*/
String formatDiff(A actual, E expected);
}Install with Tessl CLI
npx tessl i tessl/maven-com-google-truth--truth