Skip to content

Releases: assertj/assertj

v3.26.3

09 Jul 18:07
Compare
Choose a tag to compare

🧩 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) with assertThatTemporal(Temporal) #3519

🐛 Bug Fixes

Core

  • Fix Javadoc rendering on FactoryBasedNavigableListAssert::assertThat
  • Allow ComparingNormalizedFields instances to be reused across different assertions #3493

🔨 Dependency Upgrades

Core

  • Upgrade to Byte Buddy 1.14.18 #3531
  • Upgrade to JUnit BOM 5.10.3 #3525

Guava

  • Upgrade to Guava 33.2.1-jre #3499

❤️ Contributors

Thanks to all the contributors who worked on this release:

@genuss

v3.26.0

26 May 13:22
Compare
Choose a tag to compare

💥 Breaking Changes

Core

  • Delegate OptionalDouble value comparison to Double.compare in hasValue assertion #3411

    Details

    This fixes the comparison of NaN values which wasn't working the way the hasValue 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 of Assertions.assertThat(Object)
  • Deprecate AssertionErrorFactory in favor of AssertionErrorCreator
  • Deprecate catchThrowableOfType(ThrowingCallable, Class) in favor of catchThrowableOfType(Class, ThrowingCallable) #2823
  • Deprecate assertThat(Iterable, AssertFactory), assertThat(Iterable, Class) and their respective then variants #3453

✨ New Features

Core

  • Support multiple AfterAssertionErrorCollected callbacks #3313
  • Add InstanceOfAssertFactory for Set instances #3325
  • Add doesNotContainKey and doesNotContainKeys to Guava Multimap assertions #3334
  • Add assertions for JDK YearMonth type #3142
  • Add TemporalAssert type #3404
  • Add ignoringFieldsOfTypesMatchingRegexes #3369
  • Add fail(Throwable) and fail() variants #3204
  • Add isPrivate to Class assertions
  • Add doesNot[Start/End]WithWhitespace methods to CharSequence assertions #3441
  • Add createAssert(ValueProvider) to AssertFactory #3377
  • Add values() navigation method to AbstractMapAssert #3297
  • Add bytes()/bytes(Charset)/bytes(String) navigation methods to AbstractStringAssert #3232
  • Add doesNotThrowAnyExceptionExcept to AbstractThrowableAssert #3261
  • Add hasPermittedSubclasses to Class assertions #3316
  • Add isUnmodifiable to Iterator assertions #3477

🐛 Bug Fixes

Core

  • Preserve original order of elements when returning duplicates on doesNotHaveDuplicates #3333
  • Make isNotEqualTo(boolean) pass when actual is null #3343
  • Fix isEqualTo comparison of Timestamp instances with Instant #3410
  • Fix Instant conversion with Date 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 in maven-javadoc-plugin

Core

  • Fix typo in Javadoc #3365
  • Improve AssertFactory Javadoc
  • Add Throwable stack trace to ShouldHaveCauseExactlyInstance #3351
  • Fix typo #3422
  • Add Throwable stack trace to ShouldHaveCauseInstance #3392
  • Implement boolean assertions directly in AbstractBooleanAssert and remove Booleans internal class
  • Remove stack trace elements triggered by AssertJ in addition to AssertJ elements #3449

🔨 Dependency Upgrades

Core

  • Upgrade to Byte Buddy 1.14.16 #3487
  • Upgrade to JUnit BOM 5.10.2 #3357

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

04 Feb 22:04
Compare
Choose a tag to compare

🐛 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:

@ash211

v3.25.2

24 Jan 13:53
Compare
Choose a tag to compare

🐛 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 when useOverriddenEquals is enabled #3320
  • satisfiesExactlyInAnyOrder fails if actual overrides equals #3339
  • Avoid calling actual.hashCode() and expected.hashCode() in DualValue #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:

@manusa @davidboden

v3.25.1

02 Jan 23:48
Compare
Choose a tag to compare

🧩 Binary Compatibility

The release is:

  • Binary compatible with the previous minor version.
  • Binary incompatible with the previous patch version.

🐛 Bug Fixes

Core

  • Revert "Provide value when assertThatThrownBy/thenThrownBy fail" #3318
  • Revert "fix: containsExactly does not work properly with maps not using equals to compare keys" #3321

v3.25.0

31 Dec 16:11
Compare
Choose a tag to compare

🚫 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 of asInstanceOf #3138

✨ New Features

Core

  • Add 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

    Details

    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 and com.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 when actual contains null and Objects::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

Core

  • Use 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

    Details

    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

🔨 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

10 Sep 14:47
Compare
Choose a tag to compare

🐛 Bug Fixes

Core

  • Remove unintentional @Deprecated from containsOnlyDigits of CharSequence assertions #2909
  • Fix a regression that caused NPE on contains of Iterable assertions when the iterable under test is an anonymous subclass of Collection #2906

v3.24.1

10 Sep 14:46
Compare
Choose a tag to compare

🐛 Bug Fixes

Core

  • Fix a regression that caused NPE on returns / doesNotReturn when the expected value is null #2901

v3.24.0

12 Sep 17:18
Compare
Choose a tag to compare

🚫 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 and hasNoNullFields recursive assertions #2211

    Details

    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 test
    • ignoringFieldsMatchingRegexes(String... regexes) - the assertion ignores the fields matching the specified regexes in the object under test
    • ignoringFieldsOfTypes(Class<?>... typesToIgnore) - the assertion ignores the object under test fields of the given types
    • ignoringPrimitiveFields() - 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", "[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

    Details

    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

    Details

    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

    Details

    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

    Details

    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

    Details

    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...
Read more

v3.23.1

10 Sep 20:38
Compare
Choose a tag to compare

🐛 Bug Fixes

Core

  • Downgrade JUnit Jupiter from 5.9.0-M1 to 5.8.2 #2637