Releases: assertj/assertj
v3.26.3
🧩 Binary Compatibility
The release is:
- Binary compatible with the previous minor version.
- Binary incompatible with the previous patch version.
💥 Breaking Changes
Core
- Replace
assertThat(Temporal)
withassertThatTemporal(Temporal)
#3519
🐛 Bug Fixes
Core
- Fix Javadoc rendering on
FactoryBasedNavigableListAssert::assertThat
- Allow
ComparingNormalizedFields
instances to be reused across different assertions #3493
🔨 Dependency Upgrades
Core
Guava
- Upgrade to Guava 33.2.1-jre #3499
❤️ Contributors
Thanks to all the contributors who worked on this release:
v3.26.0
💥 Breaking Changes
Core
-
Delegate
OptionalDouble
value comparison toDouble.compare
inhasValue
assertion #3411Details
This fixes the comparison of
NaN
values which wasn't working the way thehasValue
Javadoc describes.The previous behavior can be obtained with
getAsDouble
:assertThat(OptionalDouble.of(Double.NaN).getAsDouble()).isSameAs(Double.NaN);
🚫 Deprecated
Core
- Deprecate
ObjectAssertFactory
in favor ofAssertions.assertThat(Object)
- Deprecate
AssertionErrorFactory
in favor ofAssertionErrorCreator
- Deprecate
catchThrowableOfType(ThrowingCallable, Class)
in favor ofcatchThrowableOfType(Class, ThrowingCallable)
#2823 - Deprecate
assertThat(Iterable, AssertFactory)
,assertThat(Iterable, Class)
and their respectivethen
variants #3453
✨ New Features
Core
- Support multiple
AfterAssertionErrorCollected
callbacks #3313 - Add
InstanceOfAssertFactory
forSet
instances #3325 - Add
doesNotContainKey
anddoesNotContainKeys
to GuavaMultimap
assertions #3334 - Add assertions for JDK
YearMonth
type #3142 - Add
TemporalAssert
type #3404 - Add
ignoringFieldsOfTypesMatchingRegexes
#3369 - Add
fail(Throwable)
andfail()
variants #3204 - Add
isPrivate
toClass
assertions - Add
doesNot[Start/End]WithWhitespace
methods toCharSequence
assertions #3441 - Add
createAssert(ValueProvider)
toAssertFactory
#3377 - Add
values()
navigation method toAbstractMapAssert
#3297 - Add
bytes()
/bytes(Charset)
/bytes(String)
navigation methods toAbstractStringAssert
#3232 - Add
doesNotThrowAnyExceptionExcept
toAbstractThrowableAssert
#3261 - Add
hasPermittedSubclasses
toClass
assertions #3316 - Add
isUnmodifiable
toIterator
assertions #3477
🐛 Bug Fixes
Core
- Preserve original order of elements when returning duplicates on
doesNotHaveDuplicates
#3333 - Make
isNotEqualTo(boolean)
pass whenactual
isnull
#3343 - Fix
isEqualTo
comparison ofTimestamp
instances withInstant
#3410 - Fix
Instant
conversion withDate
assertions #3467 - Rebuild default date formats used to parse strings as dates when default timezone or lenient flag changes #3382
⚡ Improvements
- Avoid duplicating
maven-javadoc-plugin
configuration and CSS files #3371 - Favor
additionalOption
entries inmaven-javadoc-plugin
Core
- Fix typo in Javadoc #3365
- Improve
AssertFactory
Javadoc - Add
Throwable
stack trace toShouldHaveCauseExactlyInstance
#3351 - Fix typo #3422
- Add
Throwable
stack trace toShouldHaveCauseInstance
#3392 - Implement boolean assertions directly in
AbstractBooleanAssert
and removeBooleans
internal class - Remove stack trace elements triggered by AssertJ in addition to AssertJ elements #3449
🔨 Dependency Upgrades
Core
Guava
- Upgrade to Guava 33.2.0-jre #3454
❤️ Contributors
Thanks to all the contributors who worked on this release:
@Achitheus @csct3434 @armandino @sbrannen @shaikhu @ryber @ranjitshinde91 @tmvlpl @valery1707 @izeye @yyytir777 @pbacz @biergit @Banuelorigni @java-coding-prodigy @vlsi @hazendaz @Kruschenstein @etrandafir93 @pbacz @dehasi
v3.25.3
🐛 Bug Fixes
- Lock
maven-clean-plugin
version for all modules
Core
- Fix a performance regression in the recursive comparison related to
FieldLocation
#3350 - Don't fail when the recursive comparison checks compared fields in collection elements #3349 (proper fix: #3354)
🔨 Dependency Upgrades
- Upgrade to Flatten Maven Plugin 1.6.0 #3335
- Upgrade to Groovy 4.0.18 #3347
- Upgrade to Hibernate Core 6.4.2.Final #3338
- Upgrade to Maven Surefire Report Plugin 3.2.5 #3330
- Upgrade to PITest Maven 1.15.6 #3348
- Upgrade to SpotBugs Maven Plugin 4.8.3.0 #3336
- Upgrade to advanced-security/maven-dependency-submission-action to 4 #3346
❤️ Contributors
Thanks to all the contributors who worked on this release:
v3.25.2
🐛 Bug Fixes
- Fix unresolvable Javadoc stylesheet URLs, simplify configuration #3324
Core
- Fix missing configuration for
MatcherAssert
soft assertions - Make deprecation notice visible in
AbstractAssert#asList
#3327 - Recursive comparison uses
equals
on root object whenuseOverriddenEquals
is enabled #3320 satisfiesExactlyInAnyOrder
fails ifactual
overridesequals
#3339- Avoid calling
actual.hashCode()
andexpected.hashCode()
inDualValue
#3340 - Recursive comparison checks for existence of fields in types that parameterize nested unordered iterables #3332
❤️ Contributors
Thanks to all the contributors who worked on this release:
v3.25.1
🧩 Binary Compatibility
The release is:
- Binary compatible with the previous minor version.
- Binary incompatible with the previous patch version.
🐛 Bug Fixes
Core
v3.25.0
🚫 Deprecated
Core
- Deprecate the following date/time related assertions in favor of
isCloseTo
:isEqualToIgnoringHours
isEqualToIgnoringMinutes
isEqualToIgnoringSeconds
isEqualToIgnoringMillis
isEqualToIgnoringNanos
isInSameHourAs
isInSameMinuteAs
isInSameSecondAs
- Deprecate
asList
in favor ofasInstanceOf
#3138
✨ New Features
Core
-
Add
Descriptable#describedAs(Supplier<String>)
-
Add
isInThePast
andisInTheFuture
toLocalDate
assertions #2933 -
Add
isInThePast
andisInTheFuture
to the missing Java 8 date/time types #2947 -
Add
isRecord
andisNotRecord
toClass
assertions #2968 -
Add
hasNullValue
anddoesNotHaveNullValue
toAtomicReferenceAssert
#2969 -
Add
asBoolean|Byte|Short|Int|Long|Float|Double
toString
assertions #2580 -
Add
hasRecordComponents
toClass
assertions #2995 -
Add getters for field path in
ComparisonDifference
#3007 -
Allow to compare
enum
andstring
fields in the recursive comparison #2616 -
Provide value when
assertThatThrownBy
/thenThrownBy
fail #3043 -
Add
isSealed
andisNotSealed
toClass
assertions #3080 -
Add
assertThatCharSequence
to disambiguate Groovy'sGString
#3132 -
Change the way the properties are collected using the
Class.getMethods()
API instead of iterating the class hierarchy usingClass.declaredMethods()
#3135 -
Add default constructor for
RecursiveComparator
#3206 -
Add
isPrimitive
andisNotPrimitive
toClass
assertions #2722 -
Add
anyOf(ThrowingConsumer...)
andallOf(ThrowingConsumer...)
#3219Details
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
🐛 Bug Fixes
Core
- Compare Java types from
javax
andcom.sun
by their equals method in recursive comparison #2928 - The recursive comparison
comparingOnlyFields
did not treat array/iterable elements as root objects #2994 - Fixes a bug when failing assertions on
DirectoryStream
types #3036 - Fix handling of null containers in the recursive assertion #3045
- Handle null values in map entry sets when formatting #3087
- Fix NPE when expected and actual field values are null #3034
- Fix NPE on
anyMatch
whenactual
containsnull
andObjects::isNull
is used as predicate #3151 - Fix misleading subsequence failure messages in string assertions #3166
- Do not ignore nested fields of types specified in
comparingOnlyFieldsOfTypes
#3207 containsExactly
does not work properly with maps not using equals to compare keys #2165- Escape percentage in
shouldContainExactly
error message #3288 - Fix recursive comparison of ignored types in unordered collection #3287
⚡ Improvements
- Add Spotless #3115
Core
-
Use Bnd feature to write resolved
bndrun
files to output folder #2902 -
Add
Throwable
stack trace toShouldHaveClause
#2872 -
Avoid copy of any
Collection
instance inIterables::assertContains
-
Add
Throwable
stack trace toShouldHaveRootCauseInstance
andShouldHaveRootCauseExactlyInstance
#2910 -
Add missing description to nested condition #2755
-
Make nestable condition factory accept conditions on super types #2905
-
Add varargs overload to
Assert::hasString
andAssert::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
andisNotEqualTo
on assertions #2921Details
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 useisEqualTo
instead. The message is somewhat confusing to the reader, sinceisEqualTo
is indeed used, and is becauseisEqualTo
internally relies on theequals
ofactual
, which is anAbstractAssert
instance and throws the exception above.isEqualTo
andisNotEqualTo
now check ifactual
is an assertion instance and raise anUnsupportedOperationException
if so, with a message suggesting to useisSameAs
andisNotSameAs
instead.This is, for example, useful for testing custom assertion types for extension libraries, where the use of
satisfies
instead ofisEqualTo
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 useinclusive
, notexclusive
#3217 -
Invoke
isNotNull
inreturns
anddoesNotReturn
#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
toassertj-core
andassertj-guava
#3311
🔨 Dependency Upgrades
Core
- Upgrade to Byte Buddy 1.14.11 #330
- Upgrade to JUnit BOM 5.10.1 #3254
- Upgrade to OpenTest4J 1.3.0 #3122
Guava
- Upgrade to Guava 32.1.3-jre #3218
❤️ Contributors
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
v3.24.2
v3.24.1
v3.24.0
🚫 Deprecated
Core
- Deprecate
ObjectAssert(AtomicReference)
#2795
✨ New Features
- Add Bill of Materials for AssertJ #2748
Core
-
Introduce
IntrospectionStrategy
for recursive comparison and assertion to allow configuring how objects are introspected (thanks @mikybars for the contribution). -
Add
allFieldsSatisfy
andhasNoNullFields
recursive assertions #2211Details
allFieldsSatisfy
verifies that a givenPredicate
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, thenallFieldsSatisfy
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 fields
Example:
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", "p.sadalage@recursive.test"); Author martinFowler = new Author("Martin Fowler", "m.fowler@recursive.test"); Author kentBeck = new Author("Kent Beck", "k.beck@recursive.test"); 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 #2711Details
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 aBar
field and both have anid
field, one can register aBiPredicate
forFoo
andBar
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 #2794Details
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 comparingPerson.name
,Person.address
and all otherPerson
fields. In caseactual
's field is null,expected
's field type will be checked to match one of the given types (we assumeactual
andexpected
fields have the same type).comparingOnlyFieldsOfTypes
can be combined withcomparingOnlyFields(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 resultingcompared 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 fieldsString foo
,Integer baz
andDouble bar
and the ignored fields ={"bar"}
set withignoringFields(String...)
that will removebar
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 #2794Details
isIn
verifies thatactual
is present in the given iterable/array, whileisNotIn
verifies thatactual
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 #2691Details
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
toClass
assertions #2611Details
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 a...