AssertJ is a library providing easy to use rich typed assertions
maven-clean-plugin
version for all modulesFieldLocation
#3350Thanks to all the contributors who worked on this release:
@ash211
MatcherAssert
soft assertionsAbstractAssert#asList
#3327equals
on root object when useOverriddenEquals
is enabled #3320satisfiesExactlyInAnyOrder
fails if actual
overrides equals
#3339actual.hashCode()
and expected.hashCode()
in DualValue
#3340Thanks to all the contributors who worked on this release:
@manusa @davidboden
The release is:
isCloseTo
:
isEqualToIgnoringHours
isEqualToIgnoringMinutes
isEqualToIgnoringSeconds
isEqualToIgnoringMillis
isEqualToIgnoringNanos
isInSameHourAs
isInSameMinuteAs
isInSameSecondAs
asList
in favor of asInstanceOf
#3138Add Descriptable#describedAs(Supplier<String>)
Add isInThePast
and isInTheFuture
to LocalDate
assertions #2933
Add isInThePast
and isInTheFuture
to the missing Java 8 date/time types #2947
Add isRecord
and isNotRecord
to Class
assertions #2968
Add hasNullValue
and doesNotHaveNullValue
to AtomicReferenceAssert
#2969
Add asBoolean|Byte|Short|Int|Long|Float|Double
to String
assertions #2580
Add hasRecordComponents
to Class
assertions #2995
Add getters for field path in ComparisonDifference
#3007
Allow to compare enum
and string
fields in the recursive comparison #2616
Provide value when assertThatThrownBy
/ thenThrownBy
fail #3043
Add isSealed
and isNotSealed
to Class
assertions #3080
Add assertThatCharSequence
to disambiguate Groovy's GString
#3132
Change the way the properties are collected using the Class.getMethods()
API instead of iterating the class hierarchy using Class.declaredMethods()
#3135
Add default constructor for RecursiveComparator
#3206
Add isPrimitive
and isNotPrimitive
to Class
assertions #2722
Add anyOf(ThrowingConsumer...)
and allOf(ThrowingConsumer...)
#3219
What was written as:
assertThat(elements)
.extracting(Element::getProperty)
.allSatisfy(p -> assertThat(p).satisfiesAnyOf(
p1 -> assertThat(p1).isNull(),
p1 -> assertThat(p1).isNotNull().extracting(Property::getId).isEqualTo(expected)
));
can now be shortened to:
assertThat(elements)
.extracting(Element::getProperty)
.allSatisfy(anyOf( // statically imported from Assertions
p1 -> assertThat(p1).isNull(),
p1 -> assertThat(p1).isNotNull().extracting(Property::getId).isEqualTo(expected)
));
Show the array/iterable under test in the assertion error message when it is not empty but should have been #3230
javax
and com.sun
by their equals method in recursive comparison #2928comparingOnlyFields
did not treat array/iterable elements as root objects #2994DirectoryStream
types #3036anyMatch
when actual
contains null
and Objects::isNull
is used as predicate #3151comparingOnlyFieldsOfTypes
#3207containsExactly
does not work properly with maps not using equals to compare keys #2165shouldContainExactly
error message #3288Use Bnd feature to write resolved bndrun
files to output folder #2902
Add Throwable
stack trace to ShouldHaveClause
#2872
Avoid copy of any Collection
instance in Iterables::assertContains
Add Throwable
stack trace to ShouldHaveRootCauseInstance
and ShouldHaveRootCauseExactlyInstance
#2910
Add missing description to nested condition #2755
Make nestable condition factory accept conditions on super types #2905
Add varargs overload to Assert::hasString
and Assert::doesNotHaveString
#2945
Improve recursive comparison performance by caching field and field names result #2979
Document performance cost of ignoring collection order
Track visited values and their comparison differences so that they can be reused #2954
Use hash code for compareUnorderedIterables
to improve performance in some common cases #3020
Improve recursive comparison error message regarding equals
methods used for JDK types #2678
Format large arrays as strings #3065
Remove null check as isArray
already performs it
Fix PrimitiveArralList
/TransformlingList
test file names #3124
Add exception for usages of isEqualTo
and isNotEqualTo
on assertions #2921
Before this change, the following code:
AbstractAssert<?, ?> assertion = assertThat(something);
assertThat(assertion).isEqualTo(assertion);
would throw an exception with a message about equals
being unsupported, suggesting to use isEqualTo
instead. The message is somewhat confusing to the reader, since isEqualTo
is indeed used, and is because isEqualTo
internally relies on the equals
of actual
, which is an AbstractAssert
instance and throws the exception above.
isEqualTo
and isNotEqualTo
now check if actual
is an assertion instance and raise an UnsupportedOperationException
if so, with a message suggesting to use isSameAs
and isNotSameAs
instead.
This is, for example, useful for testing custom assertion types for extension libraries, where the use of satisfies
instead of isEqualTo
may be desirable.
Add check for missing fields in recursive comparison where specific fields are requested for comparison
Improve efficiency of iterable string conversion #3123
Add more tests for usingRecursiveComparison
#2790
Checks that compared fields exist before running the recursive comparison #3129
Remove maven-surefire-plugin
duplicate version #3180
Remove Maven default goal #3182
Add dependency graph workflow
Normalize non-breaking spaces like regular white spaces #3120
AbstractOffsetDateTimeAssert::isBetween
parameter names should use inclusive
, not exclusive
#3217
Invoke isNotNull
in returns
and doesNotReturn
#3224
Cache node names in ComparingFields
, ComparingProperties
#3242
Cache node names in ComparingNormalizedFields
Use simpler syntax in DefaultRecursiveComparisonIntrospectionStrategy.getChildrenNodeNamesOf
Update to Contributor Covenant Code of Conduct v2.1
Correct contains
assertion Javadoc to reflect method behavior in case actual is not empty and the group of values to look for is #3256
Fix Javadoc typos #3265
Improving null-safety of isEqualToNormalizingNewlines
#2776
Speed up for BinaryDiff
performance #3193
Upgrade workflows to Java 21 #3191
Apply flatten-maven-plugin
to assertj-core
and assertj-guava
#3311
Thanks to all the contributors who worked on this release:
@bjhargrave @vlsi @Ds2994 @StefanBratanov @alex859 @ascopes @ljrmorgan @ghkim3221 @hezean @matthew-leng @etellman @hjir @amodolo @armandino @aindriu-aiven @Bananeweizen @JohnBryte @ManuelG28 @maximedezette @radistao @ykardziyaka @wouterpolet @sarajuhosova @pbacz @Gabriel-Darbord @Nacho321 @marcela-cardona-s @matthiaskraaz @quaff @grigala @martinfrancois @georgebax
ObjectAssert(AtomicReference)
#2795Introduce IntrospectionStrategy
for recursive comparison and assertion to allow configuring how objects are introspected (thanks @mikybars for the contribution).
Add allFieldsSatisfy
and hasNoNullFields
recursive assertions #2211
allFieldsSatisfy
verifies that a given Predicate
is met for all the fields in the field graph of the object under test (i.e., each field is evaluated recursively), but not for the object under test itself.
For example, if actual
is an instance of class A, A has a B field, and B has a C field, then allFieldsSatisfy
checks A’s B field and B’s C field, and all C’s fields.
hasNoNullFields
verifies that none of the fields are null in the field graph of the object under test (i.e., each field is evaluated recursively), but not the object under test itself.
It is possible to exclude some fields with any of these methods:
ignoringFields(String... fieldsToIgnore)
- the assertion ignores the specified fields in the object under testignoringFieldsMatchingRegexes(String... regexes)
- the assertion ignores the fields matching the specified regexes in the object under testignoringFieldsOfTypes(Class<?>... typesToIgnore)
- the assertion ignores the object under test fields of the given typesignoringPrimitiveFields()
- avoid running the assertion on primitive fieldsExample:
class Author {
String name;
String email;
List<Book> books = new ArrayList<>();
Author(String name, String email) {
this.name = name;
this.email = email;
}
}
class Book {
String title;
Author[] authors;
Book(String title, Author[] authors) {
this.title = title;
this.authors = authors;
}
}
Author pramodSadalage = new Author("Pramod Sadalage", "[email protected]");
Author martinFowler = new Author("Martin Fowler", "[email protected]");
Author kentBeck = new Author("Kent Beck", "[email protected]");
Book noSqlDistilled = new Book("NoSql Distilled", new Author[] {pramodSadalage, martinFowler});
pramodSadalage.books.add(noSqlDistilled);
martinFowler.books.add(noSqlDistilled);
Book refactoring = new Book("Refactoring", new Author[] {martinFowler, kentBeck});
martinFowler.books.add(refactoring);
kentBeck.books.add(refactoring);
// assertion succeeds
assertThat(pramodSadalage).usingRecursiveAssertion()
.allFieldsSatisfy(field -> field != null);
// best rewritten with `hasNoNullFields()`
assertThat(pramodSadalage).usingRecursiveAssertion()
.hasNoNullFields();
Add withEqualsForFieldsMatchingRegexes
to the recursive comparison #2711
Allows registering a BiPredicate
to compare fields whose location matches the given regexes.
A typical usage consists of comparing double/float fields with a given precision.
The fields are evaluated from the root object. For example, if Foo
has a Bar
field and both have an id
field, one can register a BiPredicate
for Foo
and Bar
id
by calling:
withEqualsForFieldsMatchingRegexes(idBiPredicate, ".*id")
or:
withEqualsForFieldsMatchingRegexes(idBiPredicate, "foo.*id")
Example:
class TolkienCharacter {
String name;
double height;
double weight;
}
TolkienCharacter frodo = new TolkienCharacter("Frodo", 1.2, 40);
TolkienCharacter tallerFrodo = new TolkienCharacter("Frodo", 1.3, 40.5);
BiPredicate<Double, Double> closeEnough = (d1, d2) -> Math.abs(d1 - d2) <= 0.5;
// assertion succeeds as both weight and height diff is less than 0.5
assertThat(frodo).usingRecursiveComparison()
.withEqualsForFieldsMatchingRegexes(closeEnough, ".eight")
.isEqualTo(tallerFrodo);
Add comparingOnlyFieldsOfTypes
to the recursive comparison #2794
Makes the recursive comparison to only compare given actual fields of the specified types and their subfields (no other fields will be compared).
Specifying a field of type will make all its subfields to be compared. For example, specifying the Person
type will lead to comparing Person.name
, Person.address
and all other Person
fields. In case actual
's field is null, expected
's field type will be checked to match one of the given types (we assume actual
and expected
fields have the same type).
comparingOnlyFieldsOfTypes
can be combined with comparingOnlyFields(String...)
to compare fields of the given types or names (union of both sets of fields).
comparingOnlyFieldsOfTypes
can be also combined with ignoring fields or compare only fields by name methods to restrict further the fields actually compared, the resulting compared fields = {specified compared fields of types} - {specified ignored fields}
.
For example, if the specified compared fields of types are {String.class, Integer.class, Double.class}
, when there are fields String foo
, Integer baz
and Double bar
and the ignored fields = {"bar"}
set with ignoringFields(String...)
that will remove bar
field from comparison, then only {foo, baz}
fields will be compared.
Example:
class Person {
String name;
double height;
Home home = new Home();
}
class Home {
Address address = new Address();
}
class Address {
int number;
String street;
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person moriarty = new Person("Moriarty", 1.80);
moriarty.home.address.street = "Butcher Street";
moriarty.home.address.number = 221;
// assertion succeeds as it only compared fields height and home.address.number since their types match compared types
assertThat(sherlock).usingRecursiveComparison()
.comparingOnlyFieldsOfTypes(Integer.class, Double.class)
.isEqualTo(Moriarty);
// assertion fails as home.address.street fields differ (Home fields and its subfields were compared)
assertThat(sherlock).usingRecursiveComparison()
.comparingOnlyFieldsOfTypes(Home.class)
.isEqualTo(moriarty);
Add isIn
/ isNotIn
assertions to the recursive comparison #2794
isIn
verifies that actual
is present in the given iterable/array, while isNotIn
verifies that actual
is not present in the given iterable/array. Both compare values with the recursive comparison.
class Person {
String name;
double height;
Home home = new Home();
}
class Home {
Address address = new Address();
}
class Address {
int number;
String street;
}
Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.ownedSince = new Date(123);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;
Person sherlock2 = new Person("Sherlock", 1.80);
sherlock2.home.ownedSince = new Date(123);
sherlock2.home.address.street = "Baker Street";
sherlock2.home.address.number = 221;
Person watson = new Person("Watson", 1.70);
watson.home.ownedSince = new Date(123);
watson.home.address.street = "Baker Street";
watson.home.address.number = 221;
Person moriarty = new Person("Moriarty", 1.80);
moriarty.home.ownedSince = new Date(123);
moriarty.home.address.street = "Butcher Street";
moriarty.home.address.number = 221;
// assertion succeeds as sherlock and sherlock2 data are the same but not for watson and moriarty
assertThat(sherlock).usingRecursiveComparison()
.isIn(sherlock2, Moriarty)
.isNotIn(watson, moriarty);
Add RecursiveComparator
that uses the recursive comparison for any assertions
Add satisfiesOnlyOnce
to iterable, array, and atomic array assertions #2691
Verifies that there is exactly one element in the iterable/array under test that satisfies the given Consumer
.
Example:
List<String> starWarsCharacterNames = List.of("Luke", "Leia", "Yoda");
// these assertions succeed:
assertThat(starWarsCharacterNames).satisfiesOnlyOnce(name -> assertThat(name).contains("Y")) // matches only "Yoda"
.satisfiesOnlyOnce(name -> assertThat(name).contains("Lu")) // matches only "Luke"
.satisfiesOnlyOnce(name -> assertThat(name).contains("Le")); // matches only "Leia"
// this assertion fails because the requirements are satisfied two times
assertThat(starWarsCharacterNames).satisfiesOnlyOnce(name -> assertThat(name).contains("a")); // matches "Leia" and "Yoda"
// this assertion fails because no element contains "Han"
assertThat(starWarsCharacterNames).satisfiesOnlyOnce(name -> assertThat(name).contains("Han"));
Add isAssignableTo
to Class
assertions #2611
Verifies that the Class
under test is assignable to the given one.
Example:
class Jedi {}
class HumanJedi extends Jedi {}
// this assertion succeeds
assertThat(HumanJedi.class).isAssignableTo(Jedi.class);
// this assertion fails
assertThat(Jedi.class).isAssignableTo(HumanJedi.class);
Add withThrowableThat
to ease Future
/ CompletableFuture
throwable assertions #2704
Returns a ThrowableAssertAlternative
to chain assertions on the underlying throwable for Future
/ CompletableFuture
assertions.
Example:
CompletableFuture<Void> completableFuture = new CompletableFuture<>();
completableFuture.completeExceptionally(new RuntimeException("boom!"));
then(completableFuture).failsWithin(Duration.ofMillis(1))
.withThrowableThat()
.isInstanceOf(ExecutionException.class)
.withMessageContaining("boom!");
Add matchesSatisfying
for checking pattern followed by assertion on matcher groups #2723
Verifies that the actual CharSequence
matches the given regular expression pattern, then accepts the given Consumer<Matcher>
to do further verification on the matcher.
Example:
assertThat("Frodo").matchesSatisfying("..(o.o)", matcher -> assertThat(matcher.group(1)).isEqualTo("odo"));
// same assertion but with a Pattern
Pattern pattern = Pattern.compile("..(o.o)");
assertThat("Frodo").matchesSatisfying(pattern, matcher -> assertThat(matcher.group(1)).isEqualTo("odo"));
Add NestableCondition
#2726
NestableCondition::nestable
is a building block defining a more precise condition on complex objects. It allows the creation of readable assertions and produces nicer assertion error messages.
Example:
import static org.assertj.core.condition.NestableCondition.nestable;
import static org.assertj.core.condition.VerboseCondition.verboseCondition;
class Customer {
final String name;
final Address address;
Customer(String name, Address address) {
this.name = name;
this.address = address;
}
}
class Address {
final String firstLine;
final String postcode;
Address(String firstLine, String postcode) {
this.firstLine = firstLine;
this.postcode = postcode;
}
}
static Condition<Customer> name(String expected) {
return new Condition<>(
it -> expected.equals(it.name),
"name: " + expected
);
}
static Condition<Customer> customer(Condition<Customer>... conditions) {
return nestable("person", conditions);
}
static Condition<Address> firstLine(String expected) {
return new Condition<>(
it -> expected.equals(it.firstLine),
"first line: " + expected
);
}
static Condition<Address> postcode(String expected) {
return new Condition<>(
it -> expected.equals(it.postcode),
"postcode: " + expected
);
}
static Condition<Customer> address(Condition<Address>... conditions) {
return nestable(
"address",
customer -> customer.address,
conditions
);
}
An assertion written like:
assertThat(customer).is(
customer(
name("John"),
address(
firstLine("3"),
postcode("KM3 8SP")
)
)
);
will produce an easy-to-read assertion error:
Expecting actual:
org.assertj.core.condition.Customer@27ff5d15
to be:
[✗] person:[
[✓] name: John,
[✗] address:[
[✗] first line: 3,
[✓] postcode: KM3 8SP
]
]
For an even better assertion error, see VerboseCondition
.
Add isAlphabetic
to CharSequence
assertions #2731
Verifies that the actual CharSequence
is alphabetic by checking it against the \p{Alpha}+
regex pattern POSIX character classes (US-ASCII only).
Example:
// assertions will pass
assertThat("lego").isAlphabetic();
assertThat("a").isAlphabetic();
assertThat("Lego").isAlphabetic();
// assertions will fail
assertThat("1").isAlphabetic();
assertThat(" ").isAlphabetic();
assertThat("").isAlphabetic();
assertThat("L3go").isAlphabetic();
Add isAlphanumeric
to CharSequence
assertions #2731
Verifies that the actual CharSequence
is alphanumeric by checking it against the \p{Alnum}+
regex pattern POSIX character classes (US-ASCII only).
Example:
// assertions will pass
assertThat("lego").isAlphanumeric();
assertThat("a1").isAlphanumeric();
assertThat("L3go").isAlphanumeric();
// assertions will fail
assertThat("!").isAlphanumeric();
assertThat("").isAlphanumeric();
assertThat(" ").isAlphanumeric();
assertThat("L3go!").isAlphanumeric();
Add isASCII
to CharSequence
assertions #2731
Verifies that the actual CharSequence
is ASCII by checking it against the \p{ASCII}+
regex pattern POSIX character classes (US-ASCII only).
Example:
// assertions will pass
assertThat("lego").isASCII();
assertThat("a1").isASCII();
assertThat("L3go").isASCII();
// assertions will fail
assertThat("").isASCII();
assertThat("♪").isASCII();
assertThat("⌃").isASCII();
assertThat("L3go123⌃00abc").isASCII();
Add isHexadecimal
to CharSequence
assertions #2731
Verifies that the actual CharSequence
is hexadecimal by checking it against the \p{XDigit}+
regex pattern POSIX character classes (US-ASCII only).
Example:
// assertions will pass
assertThat("A").isHexadecimal();
assertThat("2").isHexadecimal();
// assertions will fail
assertThat("!").isHexadecimal();
assertThat("").isHexadecimal();
assertThat(" ").isHexadecimal();
assertThat("Z").isHexadecimal();
assertThat("L3go!").isHexadecimal();
Add isPrintable
to CharSequence
assertions #2731
Verifies that the actual CharSequence
is printable by checking it against the \p{Print}+
regex pattern POSIX character classes (US-ASCII only).
Example:
// assertions will pass
assertThat("2").isPrintable();
assertThat("a").isPrintable();
assertThat("~").isPrintable();
assertThat("").isPrintable();
// assertions will fail
assertThat("\t").isPrintable();
assertThat("§").isPrintable();
assertThat("©").isPrintable();
assertThat("\n").isPrintable();
Add isVisible
to CharSequence
assertions #2731
Verifies that the actual CharSequence
is visible by checking it against the \p{Graph}+
regex pattern POSIX character classes (US-ASCII only).
Example:
// assertions will pass
assertThat("2").isVisible();
assertThat("a").isVisible();
assertThat(".").isVisible();
// assertions will fail
assertThat("\t").isVisible();
assertThat("\n").isVisible();
assertThat("").isVisible();
assertThat(" ").isVisible();
Add isUnmodifiable
to Map
assertions #2458
Verifies that the map under test is unmodifiable, i.e. throws an UnsupportedOperationException
with any attempt to modify the map.
Example:
// assertions succeeds
assertThat(Collections.unmodifiableMap(new HashMap<>())).isUnmodifiable();
// assertions fails
assertThat(new HashMap<>()).isUnmodifiable();
Add isExecutable
to File
assertions #2829
Verifies that the file under test is executable.
Example:
File tmpFile = java.nio.file.Files.createTempFile("executable_file", ".sh").toFile();
tmpFile.setExecutable(true);
// assertions succeeds
assertThat(tmpFile).isExecutable();
tmpFile.setExecutable(false);
// assertions fails
assertThat(tmpFile).isExecutable();
Add hasMessageMatching(Pattern)
to Throwable
assertions #2853
Verifies that the message of the Throwable
under test matches the given Pattern
regular expression.
Example:
Throwable throwable = new IllegalArgumentException("wrong amount 123");
// assertion succeeds
assertThat(throwable).hasMessageMatching(Pattern.compile("wrong amount [0-9]*"));
// assertion fails
assertThat(throwable).hasMessageMatching(Pattern.compile("wrong amount [0-9]* euros"));
sun.*
packages #2891returns
and doesNotReturn
#2725ignoringOverriddenEqualsForFieldsMatchingRegexes
that was checking field class instead of the field pathisThrownBy
#2699isCloseTo
Javadoc example for Date
assertions #2890CharSequence
#2735containsExactly
error message by printing the indices where a mismatch occurred #2638org.opentest4j.AssertionFailedError
for containsExactly
to get the actual
vs. expected
diff in the IDE #2697assertIsEqualToNormalizingNewlines
#2677within(long, TemporalUnit)
#2801CaseInsensitiveStringComparator
delegate to compareToIgnoreCase
#2663Map
type when invoking copying constructor #2710isBetween
assertion performance by only building the error message when needed #2884assertContains
when actual
is already a Collection
#2220org.assertj.core.internal.Dates
#2763RealNumbers
constructors' visibility to protected
#2772getParameterCount
rather than getParameterTypes().length
#2813setup-java
action #2802public
modifier to fix some sonar violationsMatcherShouldMatch_create_Test
and StandardRepresentation_toStringOf_AtomicReferences_Test
to JUnit JupiterBDDAssertions_then_Test.java
#2644-Xlint:all
#2648add-label
to fail in binary-compatibility.yml
#2667given(Duration)
in BDDAssumptionsTest
#2686Path
tests #2826File
tests #2827MapEntry::hashCode
oracle-actions/setup-java
for cross-version workflow #2658Thanks to all the contributors who worked on this release:
@ascopes @nstdio @Bananeweizen @mbaechler @StefanBratanov @stevenschlansker @jessicant @MartinWitt @nith2001 @WojciechZankowski @drakulavich @ChrisZoet @DirkToewe @3flex @opwvhk @alex859 @Giovds @NeshkaD @snuyanzin @rostIvan @remika @micd
Special thanks to those who contributed many internal improvements to our test suite, much appreciated!