Release Notes

AssertJ Core would not exist without its contributors, you can find them all directly on GitHub.

The latest release notes can be found in the GitHub releases.

Even older release notes can be found in the old site: joel-costigliola.github.io/assertj/assertj-core-news.html, this is important to be aware of breaking changes if you are migrating from an old version.

AssertJ Core 3.23.0

Release date: 2022-05-31

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, BJ Hargrave, Jeremy Landis, Ashley Scopes, Roland Weisleder , Benedikt Bogason , Andreas Kutschera , Matthew , Chris HeZean , Leo0506 , Zhou Yicheng , Saria , Chunhao Liao , maxwell142857 , Jessica Hamilton , nith2001 , Arman Sharif , Yuta Saito , Minami Yoshihiko , Martin Witt , Wojciech Zankowski , Gatien Bovyn , Flora Zheng , Natalia Struharova , Sára Juhošová , Pawel , Diego Krupitza , Jiashu Zhang , YeeTone Wang , Yitong , Anugrah Singhal , Stefan Bratanov , and Almir James Lucena .

Binary compatibility

The release is binary compatible with the previous minor version.

New features

  • Add hasYear, hasMonth, hasMonthValue and hasDayOfMonth LocalDate assertions (#2541). (Jessica Hamilton and nith2001)

  • Add hasHour, hasMinute, hasSecond and hasNano LocalTime assertions (#2541). (Saria, Chunhao Liao, Jessica Hamilton and maxwell142857)

  • Add hasYear, hasMonth, hasMonthValue, hasDayOfMonth, hasHour, hasMinute, hasSecond and hasNano LocalDateTime assertions (#2541). (Leo0506, Chris HeZean, Chunhao Liao, Zhou Yicheng, Yitong and Matthew)

  • Add ignoring case variants of startsWith, endsWith, doesNotStartWith, and doesNotEndWith to CharSequence assertions. (Benedikt Bogason)

  • Add linesOf variants to load a Path content. (#2618). (Stefan Bratanov)

  • Add hasFileSystem and hasSameFileSystemAs Path assertions. (Ashley Scopes)

  • Add binaryContent Path and File assertions. (Ashley Scopes)

  • Add more assertThat…​Exception alternatives (#2454). (Minami Yoshihiko)

  • Add isStatic/isNotStatic class assertions (#2455). (Wojciech Zankowski)

  • Add containsIgnoringNewLines String assertion. (Flora Zheng)

  • Add BigDecimal scale assertion. (Almir James Lucena)

  • Add assertThatExceptionOfType/thenExceptionOfType to Soft/BDDSoft assertions.

  • Add java.util.regex.Matcher assertions class with matches assertion. (Jiashu Zhang)

  • Add hasNumberOfRows to two-dimensional array assertions. (Wojciech Zankowski)

  • Sync assumptions and assertions methods. (Wojciech Zankowski)

  • Expose ComparisonStrategy::areEqual in AbstractAssert. (Stefano Cordio)

Improvements

  • Improved containsExactly performance from O(n2) to O(n) when the assertion succeeds (#2548). (nith2001, Sára Juhošová)

  • Display first stack trace elements of failures in AssertJMultipleFailuresError to ease code navigation.

  • Add assertThat{Interface}/then{Interface} methods like assertThatIterable to address potential ambiguous resolution.

  • Replace for loop with enhanced for loop (#2501). (Diego Krupitza)

  • Remove unnecessary unboxing in ComparatorFactory (#2502). (Diego Krupitza)

  • Update MapEntry::hashCode to honor Map.Entry::hashCode contract (#2503). (BJ Hargrave)

  • Use AbstractComparableAssert as parent class for URI assertions. (Pawel)

  • Avoid ValueNode being considered as iterable while using recursive comparison. (Gatien Bovyn)

  • Improve Float/Double isCloseTo assertions and comparators to honor offset check exactly. (YeeTone Wang)

  • Improve recursive comparison error when comparing unordered iterables by show expected elements not found in actual instead of unmatched actual elements.

  • Keep assertion state with getCause and getRootCause. (Stefano Cordio)

  • Improve compatibility of assertThatComparable (#2532). (Stefano Cordio)

  • Increase test coverage for internal and api package (#2543). (Natalia Struharova)

  • Increase AbstractIntegerAssert test coverage to 100% (#2515). (Sára Juhošová)

  • Add more tests for ShortAssert, FloatAssert, BigDecimalAssert, DoubleAssert, IntegerAssert, ShortArrayAssert, IntArrayAssert, IteratorAssert, OptionalAssert, PathAssert and InputStreamAssert. (Sára Juhošová)

  • Throw AssertionError with extracting(String) if actual is null. (Stefano Cordio)

  • Keep assertion state with content assertions for File and Path. (Stefano Cordio)

  • Skip well-known JDK types in AbstractCollectionAssert::isUnmodifiable (#2599). (Stefano Cordio)

  • Remove Byte Buddy shading (#2477). (Stefano Cordio)

  • Avoid cloning MultiValueMapAdapter instances (#2549). (Stefano Cordio)

  • Javadoc improvements. (Stefano Cordio)

  • Fix typos in Javadoc of ObjectEnumerableAssert (#2624). (Roland Weisleder)

  • Internal: Update maven wrapper to 3.1.1 (#2622). (Jeremy Landis)

  • Internal: Bump maven wrapper distributionUrl to 3.8.5 (#2551). (Jeremy Landis)

  • Internal: Align Javadoc stylesheet to be compatible with Java 17. (Stefano Cordio)

  • Internal: Add Gitpod configuration. (Stefano Cordio)

  • Internal: Open user specific fork on Gitpod. (Anugrah Singhal)

  • Internal: Verify binary compatibility with latest release also with PRs (#2613). (Stefano Cordio)

  • Internal: Change visibility of constructor in final util class to private (#2465). (Martin Witt)

  • Internal: Update license headers to 2022. (Stefano Cordio)

  • Internal: Add build with Java 19 EA. (Stefano Cordio)

  • Internal: Add binary compatibility result to job summary. (Stefano Cordio)

  • Internal: Use Java 18 GA in build (Stefano Cordio)

  • Internal: Use Java 17 in workflows (#2584) (Stefano Cordio)

  • Internal: Fix Maven Central badge. (Stefano Cordio)

  • Internal: Add navigation method tests to extracting variants. (Stefano Cordio)

  • Internal: Apply binary incompatible label to PRs (#2478). (Stefano Cordio)

  • Internal: Refactor test verifying that Assertions, BDDAssertions, WithAssertions and soft assertions are in sync.

  • Internal: Bump Bnd version to 6.2.0. (BJ Hargrave)

  • Internal: Bump spring-core from 5.3.14 to 5.3.20

  • Internal: Bump jboss-logging from 3.4.2.Final to 3.5.0.Final

  • Internal: Bump byte-buddy.version from 1.12.6 to 1.12.10

  • Internal: Bump github/codeql-action from 1 to 2

  • Internal: Bump pitest-maven from 1.7.5 to 1.8.0

  • Internal: Bump equalsverifier from 3.7.1 to 3.10

  • Internal: Bump parent POM to version 2.2.14 (Stefano Cordio)

  • Internal: Bump Mockito to version 4.5.1 (Stefano Cordio, Erhard Pointl)

  • Internal: Bump jacoco-maven-plugin.version from 0.8.7 to 0.8.8 (Erhard Pointl)

  • Internal: Bump hibernate-core from 6.0.0.Beta3 to 6.0.2.Final

  • Internal: Bump japicmp-maven-plugin from 0.15.4 to 0.15.7

  • Internal: Bump pitest-maven from 1.7.3 to 1.7.5

  • Internal: Bump actions/github-script from 5 to 6

  • Internal: Bump actions/checkout from 2 to 3 (Stefano Cordio)

  • Internal: Bump actions/upload-artifact from 2 to 3 (Stefano Cordio)

  • Internal: Bump actions/setup from 2 to 3

  • Internal: Bump guava from 31.0.1-jre to 31.1-jre

  • Internal: Bump jackson-databind from 2.13.1 to 2.13.3

  • Internal: Bump org.eclipse.osgi from 3.17.100 to 3.17.200

  • Internal: Bump cdg.pitest.version from 0.1.0 to 0.2.0

  • Internal: Bump pitest-github-maven-plugin from 0.1.0 to 0.1.3

  • Internal: Bump github/codeql-action from 1 to 2

  • Internal: Bump maven-invoker-plugin from 3.2.2 to 3.3.0

Fixed

  • Update docs describing passing Fail.fail() as a lambda (#2512). (Arman Sharif)

  • Fix recursive comparison that can throw IllegalStateException when comparing Maps

  • Recursive comparison did not honor comparingOnlyFields when getting actual fields to compare (#2610). (Andreas Kutschera)

  • Fix comparingOnlyFields that was greedily evaluating fields to compare.

  • Fix usingRecursiveComparison with usingOverriddenEquals ignores equals method of the root object (#2479). (Yuta Saito)

  • Fix error in Javadoc. (Yuta Saito)

  • As Java 17 forbids it, don’t use reflection to compare java types in the recursive comparison, use equals even if not overridden (#2450).

  • Recursive comparison: Compare Atomic types embedded value recursively (value is not accessed by reflection) (#2466).

  • Fix pom url to match the actual url to the doc (#2514). (BJ Hargrave)

  • Fix file names to prevent MacOS casing issues (#2534). (Sára Juhošová)

  • Disable setup-java cache to fix some cache corruption. (Stefano Cordio)

  • Fix ShouldBeSame error message that inverted actual and expected (#2565). (Stefano Cordio)

  • Fix Sonar bug. (Stefano Cordio)

Deprecated

  • Deprecate getCause and getRootCause for Throwable assertions

Add more assertThat…​Exception alternatives

Add more ThrowableTypeAssert entry point methods for commonly used exception:

  • assertThatException

  • assertThatRuntimeException

  • assertThatReflectiveOperationException

  • assertThatIndexOutOfBoundsException

This allow to check that piece of code expressed as a ThrowingCallable throws the proper exception type and chain additional assertions.

Example:

// succeeds
assertThatRuntimeException().isThrownBy(() -> {
  throw new RuntimeException("boom");
}).withMessage("boom");

// fails
assertThatRuntimeException().isThrownBy(() -> {
  throw new IOException();
})

Add linesOf variants to load a Path content

Loads the text content of a file at a given path into a list of strings with the given charset or the default charset if none is specified.

Each string corresponding to a line, the line endings are either \n, \r or \r\n.

Example:

// Terry-Pratchett.txt content:
// I'll be more enthusiastic about encouraging thinking outside the box ...
// ...when there's evidence of any thinking going on inside it.
Path pratchettQuotePath = Paths.get("Terry-Pratchett.txt");

assertThat(linesOf(pratchettQuotePath)).contains("I'll be more enthusiastic about encouraging thinking outside the box ...",
                                                 "...when there's evidence of any thinking going on inside it");

Add isStatic/isNotStatic class assertions

Add assertions to verify whether a class is static or not.

Example:

class MyClass {
  static class MyStaticClass {}
}

assertThat(MyClass.class).isNotStatic();
assertThat(MyStaticClass.class).isStatic();

Add containsIgnoringNewLines string assertion

Verifies that the {@code CharSequence} under test contains all the given values ignoring new line differences.

Example:

assertThat("Gandalf\nthe\ngrey").containsIgnoringNewLines("alf")
                                .containsIgnoringNewLines("alf", "grey")
                                .containsIgnoringNewLines("thegrey")
                                .containsIgnoringNewLines("thegr\ney")
                                .containsIgnoringNewLines("t\nh\ne\ng\nr\ney");

Add BigDecimal scale assertion

Returns an assert object that allows performing assertions on the scale of the BigDecimal under test.

Once this method is called, the object under test is no longer the BigDecimal but its scale. To go back performing assertions on the BigDecimal, call returnToBigDecimal().

Example:

assertThat(new BigDecimal("3.14")).scale()
                                    .isGreaterThan(1L)
                                    .isLessThan(5L)
                                  .returnToBigDecimal()
                                    .isPositive();

Add java.util.regex.Matcher assertions class with matches assertion

Verifies that the java.util.regex.Matcher matches.

Example:

Pattern pattern = Pattern.compile("a*");
Matcher matcher = pattern.matcher("aaa");
assertThat(matcher).matches();

Add hasNumberOfRows to two-dimensional array assertions

Verifies that the actual two-dimensional array has the given number of rows.

Example:

assertThat(new int[][] {{1, 2, 3}, {4, 5, 6}}).hasNumberOfRows(2);
assertThat(new long[][] {{1}, {1, 2}, {1, 2, 3}}).hasNumberOfRows(3);

Add hasYear, hasMonth, hasMonthValue and hasDayOfMonth LocalDate assertions

Verifies that actual LocalDate is in the given year, month/monthValue or day of month.

Example:

assertThat(LocalDate.of(2000, 12, 31)).hasYear(2000)
                                      .hasMonth(Month.DECEMBER)
                                      .hasMonthValue(12)
                                      .hasDayOfMonth(31);

Add hasHour, hasMinute, hasSecond and hasNano LocalTime assertions

Verifies that actual LocalTime is in the given hour, minute, second or nano.

Example:

assertThat(LocalTime.of(23, 17, 59, 05)).hasHour(23)
                                        .hasMinute(17)
                                        .hasSecond(59)
                                        .hasNano(05);

Add hasYear, hasMonth, hasMonthValue, hasDayOfMonth, hasHour, hasMinute, hasSecond and hasNano LocalDateTime assertions

Verifies that actual LocalDateTime is in the given year, month/monthValue, day of month, hour, minute, second or nano.

Example:

assertThat(LocalDateTime.of(2000, 12, 31, 23, 17, 59, 05)).hasYear(2000)
                                                         .hasMonth(Month.DECEMBER)
                                                         .hasMonthValue(12)
                                                         .hasDayOfMonth(31)
                                                         .hasHour(23)
                                                         .hasMinute(17)
                                                         .hasSecond(59)
                                                         .hasNano(05);

Add hasFileSystem and hasSameFileSystemAs Path assertions

Verifies that a path has the given file system or the same file system as another path

Examples:

Path jarFile = Paths.get("assertj-core.jar");
FileSystem mainFileSystem = jarFile.getFileSystem();

try (FileSystem fs = FileSystems.newFileSystem(jarFile, (ClassLoader) null)) {
  Path manifestFile = fs.getPath("META-INF", "MANIFEST.MF");

  assertThat(manifestFile).hasFileSystem(fs)
                          .hasSameFileSystemAs(jarFile);
}

Add binaryContent Path/File assertions

Returns ByteArray assertions on the content of the Path/File read.

Examples:

Path xFilePath = Files.write(Paths.get("xfile.txt"), "The Truth Is Out There".getBytes());
File xFile = xFilePath.toFile();

byte[] expectedBinaryContent = "The Truth Is Out There".getBytes()
assertThat(xFilePath).binaryContent().isEqualTo(expectedBinaryContent);
assertThat(xFile).binaryContent().isEqualTo(expectedBinaryContent);

Add ignoring case variants of startsWith, endsWith, doesNotStartWith, and doesNotEndWith CharSequence assertions

Add startsWithIgnoringCase, endsWithIgnoringCase, doesNotStartWithIgnoringCase and doesNotEndWithIgnoringCase to CharSequence assertions.

Examples:

assertThat("Gandalf the grey").startsWithIgnoringCase("gandalf")
                              .startsWithIgnoringCase("Gandalf")
                              .doesNotStartWithIgnoringCase("Saroumane")
                              .endsWithIgnoringCase("Grey")
                              .endsWithIgnoringCase("grey")
                              .doesNotEndWithIgnoringCase("great");

AssertJ Core 3.22.0

Release date: 2022-01-03

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, Yusuke Mukai , Martin Tarjányi , Trang Nguyen , jbock , Annette0127, Zihan Xu , Ashley Scopes , Benjamin Ze’ev Tels , Ahmad Sadeed , temp-droid , Ilya Koshaleu , Spacca , Erik Pragt , and Jeremy Landis.

Binary compatibility

The release is not binary compatible with the previous minor version.

Breaking changes

  • Breaking change: extracting now throws an assertion error if actual is null (Fixes #2401).

  • Breaking change: extracting/map for iterables now throws an assertion error if the actual Iterable is null (Fixes #2411).

  • Breaking change: flatExtracting/flatMap for iterables now throws an assertion error if the actual Iterable is null (Fixes #2412).

New features

Improvements

  • Treat class cast exception as comparison failure in the recursive comparison. (#2434)

  • Improve Class representation for anonymous and local types (#2445). (Stefano Cordio)

  • Return null when extracting the value of an empty Optional (#2372). (Stefano Cordio)

  • Assumptions: avoid proxying methods that don’t need to be proxied.

  • Accept vararg for AbstractAssert.satisfies. (Martin Tarjányi)

  • Print offending token in error message from containsSubsequence. (jbock)

  • extracting now throws an assertion error if actual is null (Fixes #2401). (Trang Nguyen)

  • extracting/map for iterables now throws an assertion error if the actual Iterable is null (Fixes #2411). (Trang Nguyen)

  • flatExtracting/flatMap for iterables now throws an assertion error if the actual Iterable is null (Fixes #2412). (Zihan Xu)

  • Allow checking of null keys and values with maps that do not allow them (see #2379 and #2382). (Benjamin Ze’ev Tels, Stefano Cordio)

  • Better isEqualTo error messages when values are multi lined (#2366). (temp-droid)

  • Javadoc improvements. (Stefano Cordio)

  • Internal: Avoid mocks for testing File assertions (#2337). (Ahmad Sadeed)

  • Internal: Align assertContainsKeys and assertDoesNotContainKeys testing to the pattern introduced in #2167. (Stefano Cordio)

  • Internal: Add JDK 9 collection factories (#2386). (Stefano Cordio)

  • Internal: Add release workflow (#1986). (Stefano Cordio)

  • Internal: Add additional test cases to isUnmodifiable. (Stefano Cordio)

  • Internal: Exclude Guava transitive dependency from EqualsVerifier. (Stefano Cordio)

  • Internal: Bump junit-jupiter.version from 5.8.0 to 5.8.2 (Erhard Pointl)

  • Internal: Bump mockito.version from 3.12.4 to 4.2.0 (Erhard Pointl, Stefano Cordio)

  • Internal: Bump jackson-databind from 2.12.5 to 2.13.1

  • Internal: Bump byte-buddy.version from 1.11.6 to 1.12.6

  • Internal: Bump guava from 30.1.1-jre to 31.0.1-jre

  • Internal: Bump assertj-parent-pom from 2.2.13 to 2.2.14

  • Internal: Bump pitest-maven from 1.7.0 to 1.7.3

  • Internal: Bump bnd.version from 5.3.0 to 6.1.0

  • Internal: Bump japicmp-maven-plugin from 0.15.3 to 0.15.4

  • Internal: Bump Maven version from 3.8.2 to 3.8.4 (Erhard Pointl)

  • Internal: Bump spring-core from 5.3.10 to 5.3.14

  • Internal: Bump equalsverifier from 3.7.1 to 3.8.1

  • Internal: Bump org.eclipse.osgi from 3.17.0 to 3.17.100

  • Internal: Switch to the official Maven Wrapper by Apache (#2452). (Jeremy Landis)

Fixed

  • Avoid reflection when extracting Optional value that fails with Java 17 (#2364). (Stefano Cordio)

  • Fix assumptions for extracting methods using asInstanceOf

  • Fix Javadoc for containsExactlyInAnyOrderElementsOf (#2405). (Yusuke Mukai)

  • Fix DefaultAssertionErrorCollector that dismisses expected/actual fields during injection of line numbers.

  • Fall back to Map copy when cloning causes any RuntimeException (#2448). (Stefano Cordio)

Deprecated

Deprecate encodedAsBase64 / decodedAsBase64 in favor of asBase64Encoded / asBase64Decoded

Add size assertions for File

Returns an Assert object that allows performing assertions on the size of the File under test.

Once this method is called, the object under test is no longer the File but its size, to return performing assertions on the File, call returnToFile().

Example:

File file = File.createTempFile("tmp", "bin");
Files.write(file.toPath(), new byte[] {1, 1});

assertThat(file).size()
                  .isGreaterThan(1L)
                  .isLessThan(5L)
                .returnToFile()
                  .hasBinaryContent(new byte[] {1, 1});

Add message() navigation method for Throwable

A shortcut for extracting(Throwable::getMessage, as(InstanceOfAssertFactories.STRING)) which allows to extract a throwable’s message and then execute assertions on it.

Note that once you have navigated to the throwable’s message you can’t navigate back to the throwable.

Example:

Throwable throwable = new Throwable("boom!");

assertThat(throwable).message().startsWith("boo")
                               .endsWith("!");

Add singleElement for Object[] assertions

Verifies that the array under test contains a single element and allows performing assertions on that element.

The assertions can be strongly typed if given an AssertFactory parameter.

Example:

String[] babySimpsons = { "Maggie" };

// object assertions
assertThat(babySimpsons).singleElement()
                        .isEqualTo("Maggie");

// strongly typed assertions with a predefined AssertFactory
import static org.assertj.core.api.Assertions.as; // syntactic sugar
import static org.assertj.core.api.InstanceOfAssertFactories.STRING;

assertThat(babySimpsons).singleElement(as(STRING))
                        .startsWith("Mag");

Add hasNoHost to URI and URL assertions

Verifies that the actual URI/URL has no host.

Example:

assertThat(new URI("file:///home/user/Documents/hello-world.txt")).hasNoHost();
assertThat(new URL("file:///home/user/Documents/hello-world.txt")).hasNoHost();

Add doesNotReturn to Object assertions

Verifies that the object under test does not return the given expected value from the given Function, a typical usage is to pass a method reference to assert object’s property.

Wrapping the given Function with Assertions.from(Function) makes the assertion more readable.

Example:

// from is not mandatory but it makes the assertions more readable
assertThat(frodo).doesNotReturn("Bilbo", from(TolkienCharacter::getName))
                 .doesNotReturn("Bilbo", TolkienCharacter::getName) // no from :(
                 .doesNotReturn(null, from(TolkienCharacter::getRace));

AssertJ Core 3.21.0

Release date: 2021-09-20

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, Shivakumar Swamy, Iván Aguilar, Alberto Pascual, Gily H, Stefan Bischof, RGalways17, Andrey Kuzmin, Eugene Lesnov, Szymon Linowski, Julian Honnen, Almir James Lucena and Golan Levy.

Binary compatibility

The release is binary compatible with the previous minor version.

Breaking changes

  • Custom comparison now takes precedence over reference comparison in recursive comparison (Fixes #2335).

New features

Improvements

  • Add CollectionAssert hierarchy (#2315). (Stefano Cordio)

  • Change AssertJ MapEntry and Java Map.Entry representation to be key=value.

  • Improve containsEntry and containsAllEntriesOf error message when keys match but not values.

  • Improve assertion error for hasSameElementsAs(emptyList()). (RGalways17)

  • Add stack trace information of Throwable that fails hasCauseInstanceOf or hasCauseExactlyInstanceOf (#2209). (RGalways17)

  • Add isReadable as an alias of canRead for File assertions (#2249). (Alberto Pascual)

  • Add isWritable as an alias of canWrite for File assertions (#2273). (Iván Aguilar)

  • Add hasFileName as an alias of hasName for File assertions (#2247). (Shivakumar Swamy)

  • Add satisfies with ThrowingConsumer to accept consumers that throw checked exceptions (#2297).

  • Add satisfiesAnyOf with ThrowingConsumer to accept consumers that throw checked exceptions.

  • Add allSatisfy, anySatisfy, satisfiesExactly, noneSatisfy, satisfiesExactlyInAnyOrder and filteredOnAssertions with ThrowingConsumer to accept consumers that throw checked exceptions.

  • Allow specifying supertype consumers for satisfies and satisfiesAnyOf in AbstractAssert.

  • Allow configuring the preferred assumption exception (#2267).

  • Generate proper conditionDescriptionWithStatus in MappedCondition. (Stefan Bischof)

  • Use conditionDescriptionWithStatus on Join Condition. (Stefan Bischof)

  • Add type parameter to ThrowableAssert (#2311). (Stefano Cordio)

  • Sync BDDAssumptions with Assumptions (#2313). (Stefano Cordio)

  • Javadoc improvements (#2274). (Golan Levy)

  • Javadoc improvements. (Stefano Cordio)

  • Fix Javadoc warnings. (Stefano Cordio)

  • Internal: Add binary compatibility check of main against latest release (#2271). (Stefano Cordio)

  • Internal: Add binary compatibility check of branches against main (#2285). (Stefano Cordio)

  • Internal: Use custom display name generator instead of @DisplayName on each test and remove unnecessary @DisplayName annotation. (Stefano Cordio)

  • Internal: Path and File assertions refactoring. (Stefano Cordio)

  • Internal: Add additional tests for Path assertions. (Stefano Cordio)

  • Internal: Remove unnecessary clean goal in build. (Stefano Cordio)

  • Internal: Remove MockPathsBaseTest in favor of PathsBaseTest and @TempDir. (Stefano Cordio)

  • Internal: Remove memoryfilesystem. (Stefano Cordio)

  • Internal: Reorder POM based on Maven code conventions. (Stefano Cordio)

  • Internal: Use Java 17 GA, remove EOL Java 16 in CI build. (Stefano Cordio)

  • Internal: Bump junit-jupiter.version from 5.7.2 to 5.8.0. (Stefano Cordio)

  • Internal: Bump mockito.version from 3.11.1 to 3.12.4. (Stefano Cordio)

  • Internal: Bump byte-buddy version from 1.11.2 to 1.11.16

  • Internal: Bump cdg.pitest.version from 0.0.10 to 0.1.0

  • Internal: Bump Bump pitest-junit5-plugin from 0.14 to 0.15

  • Internal: Bump pitest-maven from 1.6.7 to 1.7.0

  • Internal: Bump jackson-databind from 2.12.3 to 2.12.5

  • Internal: Bump equalsverifier from 3.6.1 to 3.7.1

  • Internal: Bump commons-io from 2.10.0 to 2.11.0.

  • Internal: Bump spring-core from 5.3.8 to 5.3.10

  • Internal: Bump org.eclipse.osgi from 3.16.300 to 3.17.0

  • Internal: Bump maven version from 3.8.1 to 3.8.2. (Stefano Cordio)

Fixed

  • Add default method lookup to hasMethods (#2324). (Stefano Cordio)

  • Load default configuration after default constants values to make sure the latter are initialized.

  • Don’t use String.format to describe ComparisonDifference in case the given string has a % that must not be interpreted (#2279).

  • Fix handling of mappings to null in MappedCondition. (Stefan Bischof)

  • Fix Javadoc warnings.

Add content()/content(Charset) to Path/File assertions to allow chaining string assertions on the Path/File content

Returns String assertions on the content of the actual Path/File read with the given charset or the default charset if no charset was given.

Example with content():

File xFile = Files.write(Paths.get("xfile.txt"), "The Truth Is Out There".getBytes()).toFile();

// assertion succeeds (default charset is used to read xFile content)
assertThat(xFile).content().startsWith("The Truth Is ")
                           .endsWith("There");
// assertion fails
assertThat(xFile).content().contains("Elsewhere");

Example with content(Charset):

File utf8File = Files.write(Paths.get("utf8.txt"), "é à".getBytes()).toFile();

// assertion succeeds
assertThat(utf8File).content(StandardCharsets.UTF_8).startsWith("é")
                                                    .endsWith("à");
// assertion fails
assertThat(utf8File).content(StandardCharsets.UTF_8).contains("e");

Add hasSize(long expectedSizeInBytes) to Path assertions

Asserts that the tested Path has the given size in bytes.

Note that the actual Path must exist and be a regular file.

Examples:

Path foxPath = Files.write(Paths.get("/fox.txt"), "The Quick Brown Fox.".getBytes());

// assertion succeeds
assertThat(foxPath).hasSize(20);

// assertion fails
assertThat(foxPath).hasSize(3);

Add isMixedCase() to CharSequence assertions

Verifies that the actual CharSequence is a mixed case CharSequence, i.e., neither uppercase nor lowercase.

If actual is empty or contains only case-independent characters, the assertion will pass.

Examples:

// assertions succeed
assertThat("Capitalized").isMixedCase();
assertThat("camelCase").isMixedCase();
assertThat("rAndOMcAse1234").isMixedCase();
assertThat("1@3$567").isMixedCase();
assertThat("").isMixedCase();

// assertions fail
assertThat("I AM GROOT!").isMixedCase();
assertThat("please be quiet").isMixedCase();

Add containsAnyOf(CharSequence…​ values) to CharSequence assertions

Verifies that the actual CharSequence contains any of the given values.

Examples:

// assertion succeeds
assertThat("Gandalf the grey").containsAnyOf("grey", "black");

// assertion fails
assertThat("Gandalf the grey").containsAnyOf("white", "black");

Add hasExtension(String extension) to Path assertions

Verifies that the actual Path has the given extension.

Examples:

Path path = Paths.get("file.java");

// assertion succeeds
assertThat(path).hasExtension("java");

// assertion fails
assertThat(path).hasExtension("png");

Add hasNoExtension() to Path and File assertions

Verifies that the actual Path or File has no extension.

Examples with Path:

// assertion succeeds
assertThat(Paths.get("file")).hasNoExtension();
assertThat(Paths.get("file.")).hasNoExtension();

// assertion fails
assertThat(Paths.get("file.txt")).hasNoExtension();

Add hasScaleOf(int expectedScale) to BigDecimals assertions

Verifies the BigDecimal under test has the given scale.

Examples:

// assertions succeed
assertThat(new BigDecimal("8.00")).hasScaleOf(2);
assertThat(new BigDecimal("8.00").setScale(4)).hasScaleOf(4);

// assertion fail
assertThat(new BigDecimal("8.00")).hasScaleOf(3);
assertThat(new BigDecimal("8.00").setScale(4)).hasScaleOf(2);

Add isUnmodifiable() to Collection assertions

Verifies that the actual collection is unmodifiable, i.e., throws an UnsupportedOperationException with any attempt to modify the collection.

Example:

// assertions succeed
assertThat(Collections.unmodifiableCollection(new ArrayList<>())).isUnmodifiable();
assertThat(Collections.unmodifiableList(new ArrayList<>())).isUnmodifiable();
assertThat(Collections.unmodifiableSet(new HashSet<>())).isUnmodifiable();

// assertions fail
assertThat(new ArrayList<>()).isUnmodifiable();
assertThat(new HashSet<>()).isUnmodifiable();

AssertJ Core 3.20.2

Release date: 2021-06-20

Bugfix release that revert a breaking change in 3.20.0 due to the heavy impact on binary compatibility and to continue allowing third-party libraries returning their own assertion classes with overridden assertThat methods.

Thanks to David Schlosnagle and Stefano Cordio for the fixes in this release.

Binary compatibility

The release is not binary compatible with the previous minor version.

Fixed

  • Restore deep equality comparison for map containsOnly assertions. (David Schlosnagle)

  • Revert "[Breaking change] Align return types across assertions / assumptions / soft assertions and do not use Abstract Asserts" introduced in 3.20.0.

AssertJ Core 3.20.1

Release date: 2021-06-16

Bugfix release.

Binary compatibility

The release is not binary compatible with the previous minor version.

Fixed

  • Fix StandardComparisonStrategy.areEqual array comparison when given a non null array and a null one.

AssertJ Core 3.20.0

Release date: 2021-06-15

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, Harsha Vipparti, Julien Roy, Aakarshit Uppal, Abhijeet Shukla, Jack Gough, Filip Hrisafov, RGalways17, Stefan Birkner, Stefan Bischof, Matthieu Baechler, sustc11810424, Henry Coles, Annette0127, Johannes Becker, Slawomir Jaranowski, and Patrick Allain.

Special thanks to Filip Hrisafov to have got rid of the heap pollution compiler warning when using soft assertions or assumptions with methods having a generic vararg parameters.

Binary compatibility

The release is not binary compatible with the previous minor version.

Breaking changes

  • PropertyOrFieldSupport fails when trying to get map value for an unknown key instead of returning null, this impacts:

    • assertThat(map).hasFieldOrPropertyWithValue(name, value) and assertThat(map).hasFieldOrProperty(name) now fail if name is not a known key.

    • assertThat(map).extracting("unknown") now fails instead of returning null.

    • assertThat(map).flatExtracting("unknown1", "unknown2") now fails instead of returning a list of with null values for unknown keys.

  • assertThat(map).containsAllEntriesOf(otherMap) now succeeds when otherMap is empty instead of failing.

  • RecursiveComparisonConfiguration now uses the same default values when built from builder and constructor.

  • usingRecursiveFieldByFieldElementComparator in iterable/array/atomic reference array now uses the new recursive comparison instead of the old implementation.

  • RecursiveFieldByFieldComparator was removed as it has been replaced by the recursive comparison in usingRecursiveFieldByFieldElementComparator.

  • Public methods that could cause heap pollution have been made final to improve the user experience with soft assertions and soft assumptions. In case a subclass was overriding any of these methods, a similar result can be achieved by overriding the corresponding ForProxy method. For example, AbstractMapAssert#contains now delegates the implementation to AbstractMapAssert#containsForProxy, which is protected and therefore can be overridden.

  • Align return types across assertions / assumptions / soft assertions and return concrete types. The breaking change is for people that have implemented WithAssertions or WithAssumption as they now have different return types, the regular user should not see any difference.

New features

Improvements

  • In error messages, use 'toString' method of subclasses of AtomicReference when overridden. (sustc11810424)

  • SoftAssertionsExtension now reports line number in reported assertion errors.

  • Align return types across assertions / assumptions / soft assertions and do not use Abstract Asserts. (Filip Hrisafov)

  • Use varargs for satisfiesAnyOf to remove the multiple overloaded versions. (Filip Hrisafov)

  • Remove compiler heap pollution warnings that were raised for soft assertions or assumptions. (Filip Hrisafov)

  • Improve performance in StandardComparisonStrategy#areEqual. (Filip Hrisafov)

  • Return concrete type where proxyable types were used in Assumptions, BDDAssumptions and WithAssumptions.

  • RecursiveComparisonConfiguration now uses the same default values when built from builder and constructor.

  • Improve error message when map is not empty and expected entries is. (Abhijeet Shukla)

  • ThrowableTypeAlternative now inherits from AbstractObjectAssert as AbstractThrowableAssert did.

  • Make assertThat(map).containsAllEntriesOf(otherMap) assertion to succeed when otherMap is empty. (Aakarshit Uppal)

  • Add non assertions methods in BDDAssertions that existed in Assertions but were missing in BDDAssertions.

  • Display proper collection type in contains and containsAll error messages . (Patrick Allain)

  • Rework StandardComparisonStrategy#areEqual to avoid shortcuts. (Stefano Cordio)

  • Disambiguate error messages by adding "actual" before printing actual value. (Harsha Vipparti)

  • Recursive comparison: properly track field location for maps and honor ignored fields.

  • allOf condition error message shows state (successful/failed) of each combined conditions.

  • Javadoc improvements. (Stefano Cordio)

  • Clarify newListAssertInstance Javadoc.

  • Fix flaky test by making sure default date formats are used before tests.

  • Remove unnecessary space ShouldNotBe and ShouldNotHave error messages. (Annette0127)

  • Internal: Rename test to match its purpose. (Matthieu Baechler)

  • Internal: Fix some warnings in AssertJ codebase. (Filip Hrisafov)

  • Internal: Remove Proxyable assert classes since we don’t need to subclass assert classes to fix heap pollution with @SafeVarargs.

  • Internal: Hide implementation detail of assertContainsOnlyKeys. (Stefano Cordio)

  • Internal: Remove unnecessary Javadoc from internal class, import Map.Entry. (Stefano Cordio)

  • Internal: Favor requireNonNull with Supplier. (Stefano Cordio)

  • Internal: Update CodeQL workflow. (Stefano Cordio)

  • Internal: Mention the JDK 11 requirement in the contribution guidelines. (Stefano Cordio)

  • Internal: Remove EOL JDK versions from CI multi versions build. (Stefano Cordio)

  • Internal: Bump maven version from 3.6.3 to 3.8.1 (Stefano Cordio)

  • Internal: Bump jacoco-maven-plugin.version from 0.8.6 to 0.8.7

  • Internal: Bump guava from 30.1-jre to 30.1.1-jre

  • Internal: Bump org.eclipse.osgi from 3.16.100 to 3.16.200

  • Internal: Bump jackson-databind from 2.12.1 to 2.12.3

  • Internal: Bump commons-lang3 from 3.11 to 3.12.0

  • Internal: Bump commons-io from 2.8.0 to 2.9.0.

  • Internal: Bump mockito.version from 3.7.7 to 3.10.0 (Erhard Pointl)

  • Internal: Bump bnd.version from 5.2.0 to 5.3.0

  • Internal: Bump sonar-maven-plugin to version 3.8.0.2131 (Stefano Cordio)

  • Internal: Bump equalsverifier from 3.5.2 to 3.6.1

  • Internal: Bump spring-core from 5.3.6 to 5.3.7

  • Internal: Bump byte-buddy version from 1.10.19 to 1.11.1

  • Internal: Bump maven-invoker-plugin from 3.2.1 to 3.2.2

  • Internal: Bump junit-jupiter.version from 5.6.3 to 5.7.2 (Erhard Pointl)

  • Internal: Bump junit.version from 4.13.1 to 4.13.2 (Erhard Pointl)

  • Internal: Fix IntelliJ flaky test due to JUnit upgrade to version 5.7. (Stefano Cordio)

  • Internal: Build AssertJ Core with java 16 in CI cross version build. (Erhard Pointl)

  • Internal: Bump actions/setup-java to version 2 (Stefano Cordio)

  • Internal: Verify PRs with pitest (Henry Coles)

  • Internal: Add .DS_Store to .gitignore (Slawomir Jaranowski)

  • Internal: Enforce Java 11 or newer to build the project (Slawomir Jaranowski)

  • Internal: Define Java 9 compile execution instead of maven profile (Slawomir Jaranowski)

Fixed

  • Honor map key comparison semantics in containsOnly assertions. (Stefano Cordio and Filip Hrisafov)

  • Fix containsSubsequence String assertion failing when given multiple empty values. (Jack Gough)

  • Fix NullPointerException in primitive double assertions where a null Double is compared to a primtive one. (Jack Gough)

  • Fix NullPointerException in StandardRepresentation. (Jack Gough)

  • Make IterableDiff to always compare actual elements to expected elements and not the other way around in case the comparison is not symmetrical.

  • Add enum types to the types that can’t cause cycles in a recursive comparison.

  • Fix hasOnlyFields which should not consider static fields.

  • Fix StackOverflowError when usingRecursiveComparison of Path on Windows by using path natural comparator. (Julien Roy)

  • Extract value only if the map key exists in PropertyOrFieldSupport. (Stefano Cordio)

  • Replace references to mapOf by Guava ImmutableMap.of in Javadoc.

  • Fix Javadoc warnings.

Deprecated

The main deprecations are related to shallow field by field comparison classes and methods:

usingFieldByFieldElementComparator

Use usingRecursiveFieldByFieldElementComparator() or usingRecursiveComparison() instead to perform a true recursive comparison.

usingElementComparatorOnFields

usingElementComparatorIgnoringFields

usingComparatorForElementFieldsWithNames

This method is used with usingFieldByFieldElementComparator() which is deprecated in favor of usingRecursiveFieldByFieldElementComparator(RecursiveComparisonConfiguration) or usingRecursiveComparison().

When using usingRecursiveComparison() the equivalent is:

and when using usingRecursiveFieldByFieldElementComparator(RecursiveComparisonConfiguration config), sets the config with:

usingComparatorForElementFieldsWithType

This method is used with usingFieldByFieldElementComparator() which is deprecated in favor of usingRecursiveFieldByFieldElementComparator(RecursiveComparisonConfiguration) or usingRecursiveComparison().

When using usingRecursiveComparison() the equivalent is:

and when] using usingRecursiveFieldByFieldElementComparator(RecursiveComparisonConfiguration config), sets the config with:

Add containsIgnoringWhitespaces to String assertions

Verifies that the actual CharSequence contains all the given values, ignoring whitespace differences.

Example:

assertThat("Gandalf the grey").containsIgnoringWhitespaces("alf")
                              .containsIgnoringWhitespaces("alf", "grey")
                              .containsIgnoringWhitespaces("thegrey")
                              .containsIgnoringWhitespaces("thegr  ey")
                              .containsIgnoringWhitespaces("t h e g r\t\r\n ey");

Add IterableAssert.elements(int…​) to assert on specific elements in an Iterable

Allow to perform assertions on the elements corresponding to the given indices (the iterable Iterable under test is changed to an iterable with the selected elements).

Example:

Iterable<TolkienCharacter> hobbits = Arrays.asList(frodo, sam, pippin);

// assertion succeeds
assertThat(hobbits).elements(1, 2)
                   .hasSize(2)
                   .containsExactly(sam, pippin);

// assertion fails
assertThat(hobbits).element(1, 2)
                   .containsExactly(frodo, pippin);

Add hasExactlyElementsOfTypes assertion for iterables, arrays and AtomicReferenceArrays

Verifies that the actual elements are of the given types in the given order, there should be as many expected types as there are actual elements.

This assertion is available for iterables, arrays and AtomicReferenceArrays.

Example:

Iterable<Object> list = Arrays.asList(1, "a", "b", 1.00);

assertThat(list).hasExactlyElementsOfTypes(Integer.class, String.class, String.class, Double.class);

Add asString(charset) to AbstractInputStreamAssert to support String assertions

Converts the content of the actual InputStream to a String by decoding its bytes using the given charset and returns assertions for the computed String allowing String specific assertions from this call.

Example:

InputStream abcInputStream = new ByteArrayInputStream("abc".getBytes());

assertThat(abcInputStream).asString(UTF_8)
                          .startsWith("a");

allOf condition error message shows state (successful/failed) of each combined conditions

allOf condition error message reports which conditions failed [✗] and which succeeded [✓] to ease understanding the failure cause.

Let’s use an allOf condition checking 3 conditions: young, very tall and Jedi and try it on Yoda, it fails with the following error:

Expecting actual:
  "Yoda"
to be:
[✗] all of:[
   [✓] a Jedi,
   [✗] very tall,
   [✗] young
]

Add assertWith assertion construct

Uses the given instance as the instance under test for all the assertions expressed as the passed Consumer.

This is useful to avoid repeating getting the instance to test, a bit like a with block which turns the target into the equivalent of this (as in Groovy for example).

Example:

assertWith(team.getPlayers().get(0).getStats(),
           stats -> {
              assertThat(stats.pointPerGame).isGreaterThan(25.7);
              assertThat(stats.assistsPerGame).isGreaterThan(7.2);
              assertThat(stats.reboundsPerGame).isBetween(9, 12);
           });

assertWith is variation of AbstractAssert.satisfies(Consumer) hopefully easier to find for some users.

Add isNotFinite float and double assertions

Verifies that the double/float value is not a finite floating-point value.

Note that 'not finite' is not equivalent to infinite as NaN is neither finite or infinite.

Examples:

assertThat(Double.POSITIVE_INFINITY).isNotFinite();
assertThat(Double.NEGATIVE_INFINITY).isNotFinite();
assertThat(Double.NaN).isNotFinite();

assertThat(Float.POSITIVE_INFINITY).isNotFinite();
assertThat(Float.NEGATIVE_INFINITY).isNotFinite();
assertThat(Float.NaN).isNotFinite();

Add isNotInfinite float and double assertions

Verifies that the double/float value represents neither positive infinity nor negative infinity.

Examples with doubles:

// assertions succeed
assertThat(1.0).isNotInfinite();
assertThat(Double.NaN).isNotInfinite();

// assertions fail
assertThat(Double.POSITIVE_INFINITY).isNotInfinite();
assertThat(Double.NEGATIVE_INFINITY).isNotInfinite();

Add comparingOnlyFields float and double assertions

Makes the recursive comparison to only compare given actual fields and their subfields (no other fields will be compared).

Specifying a field will make all its subfields to be compared, for example specifying person will lead to compare person.name, person.address and all other Person fields. On the other hand if you specify person.name, person won’t be compared but person.name will be.

The fields are specified by name, not by value, for example you can specify person.name but not "Jack" as "Jack" is not a field value.

comparingOnlyFields can be combined with ignoring fields methods to restrict further the fields actually compared, the resulting compared fields = {specified compared fields} - {specified ignored fields}. For example if compared fields = {"foo", "bar", "baz"} and ignored fields = {"bar"} then only {"foo", "baz"} fields will be compared.

Example:

public class Person {
  String name;
  double height;
  Home home = new Home();
}

public class Home {
  Address address = new Address();
}

public static 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 name and home.address.street fields are not compared.
assertThat(sherlock).usingRecursiveComparison()
                    .comparingOnlyFields("height", "home.address.number")
                    .isEqualTo(moriarty);

// assertion fails as home.address.street fields differ.
assertThat(sherlock).usingRecursiveComparison()
                    .comparingOnlyFields("height", "home")
                    .isEqualTo(moriarty);

Add usingRecursiveFieldByFieldElementComparatorOnFields(String…​) to iterable/array/atomic reference array assertions to restrict the recursive comparison to the specified fields

The assertions chained after this method will use a recursive field by field comparison on the given fields (including inherited fields) instead of relying on the element equals method. This is handy when the element equals method is not overridden or implemented as you expect.

Nested fields are supported and are expressed like: name.first

The comparison is recursive: elements are compared field by field, if a field type has fields they are also compared field by field (and so on).

Example:

Player derrickRose = new Player(new Name("Derrick", "Rose"), "Chicago Bulls");
derrickRose.nickname = new Name("Crazy", "Dunks");

Player jalenRose = new Player(new Name("Jalen", "Rose"), "Chicago Bulls");
jalenRose.nickname = new Name("Crazy", "Defense");

// assertion succeeds as all compared fields match
assertThat(list(derrickRose)).usingRecursiveFieldByFieldElementComparatorOnFields("name.last", "team", "nickname.first")
                             .contains(jalenRose);

// assertion fails, name.first values differ
assertThat(list(derrickRose)).usingRecursiveFieldByFieldElementComparatorOnFields("name")
                             .contains(jalenRose);

This method is actually a shortcut of usingRecursiveFieldByFieldElementComparator(RecursiveComparisonConfiguration) with a configuration comparing only the given fields, the previous example can be written as:

RecursiveComparisonConfiguration configuration = RecursiveComparisonConfiguration.builder()
                                                                                 .withComparedFields("name.last", "team", "nickname.first")
                                                                                 .build();

assertThat(list(derrickRose)).usingRecursiveFieldByFieldElementComparator(configuration)
                             .contains(jalenRose);

Add usingRecursiveFieldByFieldElementComparatorIgnoringFields(String…​) to iterable/array/atomic reference array assertions to ignore some fields in the recursive comparison

The assertions chained after this method will use a recursive field by field comparison on all fields (including inherited fields) except the given ones instead of relying on the element equals method. This is handy when the element equals method is not overridden or implemented as you expect.

Nested fields are supported and are expressed like: name.first

The comparison is recursive: elements are compared field by field, if a field type has fields they are also compared field by field (and so on).

Example:

Player derrickRose = new Player(new Name("Derrick", "Rose"), "Chicago Bulls");
derrickRose.nickname = new Name("Crazy", "Dunks");

Player jalenRose = new Player(new Name("Jalen", "Rose"), "Chicago Bulls");
jalenRose.nickname = new Name("Crazy", "Defense");

// assertion succeeds as all compared fields match
assertThat(list(derrickRose)).usingRecursiveFieldByFieldElementComparatorIgnoringFields("name.last", "nickname.first")
                             .contains(jalenRose);

// assertion fails, names are ignored but nicknames are not and nickname.last values differ
assertThat(list(derrickRose)).usingRecursiveFieldByFieldElementComparatorIgnoringFields("name")
                             .contains(jalenRose);

This method is actually a shortcut of usingRecursiveFieldByFieldElementComparator(RecursiveComparisonConfiguration) with a configuration comparing only the given fields, the previous example can be written as:

RecursiveComparisonConfiguration configuration = RecursiveComparisonConfiguration.builder()
                                                                                 .withIgnoredFields("name.first", "nickname.last")
                                                                                 .build();

assertThat(list(derrickRose)).usingRecursiveFieldByFieldElementComparator(configuration)
                             .contains(jalenRose);

Add MappedCondition to verify a Condition on the result of a map operation

A MappedCondition is defined with a map operation and a Condition that accepts the type returned by the map operation.
When applied the MappedCondition first map the value under test and then verify the resulting mapped value against its nested Condition.

Let’s see how it works on an example:

// nested Condition
Condition<String> hasLineSeparator = new Condition<>(s -> s.contains(System.lineSeparator()), "has lineSeparator");
// mapped Condition
Condition<Optional<String>> optionalWithLineSeparator = MappedCondition.mappedCondition(Optional::get, hasLineSeparator, "optional value has lineSeparator");

// assertion succeeds
assertThat(Optional.of("a" + System.lineSeparator())).is(optionalWithLineSeparator)

// assertion fails
assertThat(Optional.of("a")).is(optionalWithLineSeparator)

Add VerboseCondition to get a detailed description when the condition fails

A VerboseCondition shows the value under test when it fails thanks to the specified objectUnderTestDescriptor function.

When defining the objectUnderTestDescriptor function, you should take in consideration whether the condition is going to be used with is(Condition) or has(Condition) since the start of the error message is different between the two.

Let’s see how it works with an example that works well with is(Condition):

Condition<String> shorterThan4 = VerboseCondition.verboseCondition(actual -> actual.length() < 4,
                                                                   // predicate description
                                                                   "shorter than 4",
                                                                   // value under test description transformation function
                                                                   s -> String.format(" but length was %s", s.length()));

If we execute:

assertThat("foooo").is(shorterThan4);

we get the following assertion error:

Expecting actual:
  "foooo"
to be shorter than 4 but length was 5

Note that the beginning of the error message looks nice with is(Condition) but not so much has(Condition):

Expecting actual:
  "foooo"
to have shorter than 4 but length was 5

AssertJ Core 3.19.0

Release date: 2021-01-24

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, Pascal Schumacher, Arsen Ibragimov, Kim S. Ly, Victor Wang, Omar Morales, Reto Weiss, Michael Florian Grafl, Sergei Tachenov, Mayra Lucero Garcia Ramírez, Eveneko, Julieta Navarro, Michael Keppler, Alex Dukhno, Himadri Mandal and Jin Kwon.

Binary compatibility

The release is not binary compatible with the previous minor version.

Breaking changes

  • Replacement of FieldLocation by String breaking changes:

    • Remove deprecated RecursiveComparisonConfiguration.registerComparatorForField(Comparator<?> comparator, FieldLocation fieldLocation) in favor of registerComparatorForFields(Comparator<?> comparator, String…​ fieldLocations)

    • getIgnoredCollectionOrderInFields(), getIgnoredFields() and getIgnoredOverriddenEqualsForFields() now return a collection of String instead of FieldLocation

    • comparatorByFields() now returns Stream<Entry<String, Comparator<?>>> instead of Stream<Entry<FieldLocation, Comparator<?>>>

  • Ignore static and synthetic fields when introspecting fields as they are not relevant in for instance assertions. This change impacts:

    • hasFieldOrProperty

    • hasFieldOrPropertyWithValue

    • extracting field by name

  • Probably not an impactful breaking change: abstract classes' constructors are now protected instead of public.

  • Return generic return type for AbstractSoftAssertions.fail for convenience and to be consistent with Assertions.fail. This is a binary incompatible but still source compatible change.

New features

Improvements

  • Change isEqualTo error message to follow pattern:

expected: "abc"
but was : "bcd"
  • Remove <> from error messages when displaying values as it was deemed noisy. (Erhard Pointl, Omar Morales, Himadri Mandal)

  • Better indentation consistency in error messages.

  • Include the first 3 stacktrace elements in throwable representation (configurable). (Eveneko)

  • Change abstract class constructor to protected.

  • Use normalized actual and expected in String assertion errors that compare normalized values. (Etienne Miret)

  • Refactor common representation code into UnambiguousRepresentation. (Etienne Miret)

  • Use org.junit.ComparisonFailure when available. (Etienne Miret)

  • Improve describe error readability of ElementsShouldSatisfy. (Drummond Dawson)

  • Recursive comparison: show the index of the array/list element compared in the reported differences.

  • Recursive comparison documentation: make it clear that ignoringFields and ignoringFieldsMatchingRegexes operate on field names.

  • Ignore getters with void return type during property introspection. (Reto Weiss)

  • Recalculate description for nested Conditions like Not and Join as some Condition description when the Condition is evaluated. (Stefan Bischof)

  • Javadoc Date assertions improvements.

  • Explain reference to newLinkedHashMap in Javadoc.

  • Internal: Only instantiate assertion error from AssertionErrorCreator. (Alex Dukhno)

  • Internal: Use parent pom 2.2.10 that bumps the licence year to 2021. (Erhard Pointl)

  • Internal: Use java11 in codeql workflow. (Erhard Pointl)

  • Internal: Fix a bunch of sonar violations. (Erhard Pointl)

  • Internal: Reduce test fragility. (Erhard Pointl)

  • Internal: Enforce surefire encoding. (Stefano Cordio)

  • Internal: Bump junit version from 4.13 to 4.13.1 (Erhard Pointl)

  • Internal: Bump highlight version to 10.4.0

  • Internal: Bump mockito version from 3.6.0 to 3.6.28 (Erhard Pointl)

  • Internal: Bump jackson-databind from 2.11.3 to 2.12.1

  • Internal: Bump guava from 30.0-jre to 30.1-jre

  • Internal: Bump org.eclipse.osgi from 3.16.0 to 3.16.100

  • Internal: Bump byte-buddy version from 1.10.18 to 1.10.19

  • Internal: Bump mockito version from 3.6.28 to 3.7.7

  • Internal: Bump equalsverifier from 3.5 to 3.5.2

Fixed

  • Fix long overflow in AbstractTemporalAssert. (Sergei Tachenov)

  • Make Collection extending AtomicInteger to be represented as a collection instead of an AtomicInteger.

  • Fix Javadoc typo. (Michael Keppler)

Deprecated

  • Deprecate isXmlEqualToContentOf in favor of XML Unit.

Add isEqualToNormalizingUnicode CharSequence assertion

Verifies that the actual CharSequence is equal to the given one after they have been normalized according to the Normalizer.Form.NFC form, which is a canonical decomposition followed by canonical composition.

Examples:

// assertions succeed:

// Ä = \u00C4 - Ä = \u0041\u0308
assertThat("Ä").isEqualToNormalizingUnicode("Ä");
assertThat("\u00C4").isEqualToNormalizingUnicode("\u0041\u0308");

// assertions fail:
assertThat("ñ").isEqualToNormalizingUnicode("n");
assertThat("Ä").isEqualToNormalizingUnicode("b");

Add satisfiesExactly iterable/array assertion

Verifies that each element satisfies the requirements corresponding to its index, so the first element must satisfy the first requirements, the second element the second requirements etc…​

Each requirement is expressed as a Consumer, and there must be as many requirements as there are iterable elements.

Examples:

Iterable<TolkienCharater> characters = list(frodo, aragorn, legolas);

// assertions succeed
assertThat(characters).satisfiesExactly(character -> assertThat(character.getRace()).isEqualTo("Hobbit"),
                                        character -> assertThat(character.isMortal()).isTrue(),
                                        character -> assertThat(character.getName()).isEqualTo("Legolas"));

// you can specify more that one assertion per requirements
assertThat(characters).satisfiesExactly(character -> {
                                           assertThat(character.getRace()).isEqualTo("Hobbit");
                                           assertThat(character.getName()).isEqualTo("Frodo");
                                        },
                                        character -> {
                                           assertThat(character.isMortal()).isTrue();
                                           assertThat(character.getName()).isEqualTo("Aragorn");
                                        },
                                        character -> {
                                           assertThat(character.getRace()).isEqualTo("Elf");
                                           assertThat(character.getName()).isEqualTo("Legolas");
                                        });

// assertion fails as aragorn does not meet the second requirements
assertThat(characters).satisfiesExactly(character -> assertThat(character.getRace()).isEqualTo("Hobbit"),
                                        character -> assertThat(character.isMortal()).isFalse(),
                                        character -> assertThat(character.getName()).isEqualTo("Legolas"));

Add satisfiesExactlyInAnyOrder iterable/array assertion

Verifies that at least one combination of iterable elements exists that satisfies the consumers in order (there must be as many consumers as iterable elements and once a consumer is matched it cannot be reused to match other elements).

This is a variation of satisfiesExactly where order does not matter.

Examples:

List<String> starWarsCharacterNames = list("Luke", "Leia", "Yoda");

// these assertions succeed:
assertThat(starWarsCharacterNames).satisfiesExactlyInAnyOrder(name -> assertThat(name).contains("Y"), // matches "Yoda"
                                                              name -> assertThat(name).contains("L"), // matches "Luke" and "Leia"
                                                              name -> {
                                                                assertThat(name).hasSize(4);
                                                                assertThat(name).doesNotContain("a"); // matches "Luke" but not "Leia"
                                                              })
                                  // satisfiesExactly would have succeeded for this assertion
                                  .satisfiesExactlyInAnyOrder(name -> assertThat(name).contains("Lu"),
                                                              name -> assertThat(name).contains("Le"),
                                                              name -> assertThat(name).contains("Yo"))
                                  // satisfiesExactly would have failed for this assertion
                                  .satisfiesExactlyInAnyOrder(name -> assertThat(name).contains("Yo"),
                                                              name -> assertThat(name).contains("Lu"),
                                                              name -> assertThat(name).contains("Le"))
                                  // satisfiesExactly would have failed for this assertion
                                  .satisfiesExactlyInAnyOrder(name -> assertThat(name).contains("Le"),
                                                              name -> assertThat(name).contains("Yo"),
                                                              name -> assertThat(name).contains("Lu"));

// this assertion fails as 3 consumer/requirements are expected
assertThat(starWarsCharacterNames).satisfiesExactlyInAnyOrder(name -> assertThat(name).contains("Y"),
                                                              name -> assertThat(name).contains("L"));

// this assertion fails as no element contains "Han" (first consumer/requirements can't be met)
assertThat(starWarsCharacterNames).satisfiesExactlyInAnyOrder(name -> assertThat(name).contains("Han"),
                                                              name -> assertThat(name).contains("L"),
                                                              name -> assertThat(name).contains("Y"));

// this assertion fails as "Yoda" element can't satisfy any consumers/requirements (even though all consumers/requirements are met)
assertThat(starWarsCharacterNames).satisfiesExactlyInAnyOrder(name -> assertThat(name).contains("L"),
                                                              name -> assertThat(name).contains("L"),
                                                              name -> assertThat(name).contains("L"));

// this assertion fails as no combination of elements can satisfy the consumers in order
// the problem is if the last consumer is matched by Leia then no other consumer can match Luke (and vice versa)
assertThat(starWarsCharacterNames).satisfiesExactlyInAnyOrder(name -> assertThat(name).contains("Y"),
                                                              name -> assertThat(name).contains("o"),
                                                              name -> assertThat(name).contains("L"));

Add Instant variants to Date assertions

Date assertions now understand Instant parameters.

Examples:

final Date dateTimeWithMs = parseDatetimeWithMs("2001-02-03T04:05:06.700");

assertThat(dateTimeWithMs).isEqualTo(dateTimeWithMs.toInstant())
                          .isBefore(Instant.parse("2002-01-01T00:00:00.00Z"))
                          .isAfter(Instant.parse("2000-01-01T00:00:00.00Z"))
                          .isBetween(Instant.parse("2000-01-01T00:00:00.00Z"),
                                     Instant.parse("2002-01-01T00:00:00.00Z"))
                          .isCloseTo(dateTimeWithMs.toInstant().minusMillis(10), 20)
                          .isEqualToIgnoringHours(dateTimeWithMs.toInstant().plus(1, ChronoUnit.HOURS))
                          .isEqualToIgnoringMinutes(dateTimeWithMs.toInstant().plus(1, ChronoUnit.MINUTES))
                          .isEqualToIgnoringSeconds(dateTimeWithMs.toInstant().plus(1, ChronoUnit.SECONDS))
                          .isEqualToIgnoringMillis(dateTimeWithMs.toInstant().plus(1, ChronoUnit.MILLIS))
                          .isIn(dateTimeWithMs.toInstant(), dateTimeWithMs.toInstant().plusMillis(10))
                          .isInSameDayAs(dateTimeWithMs.toInstant().plus(1, ChronoUnit.MINUTES))
                          .isInSameMonthAs(Instant.parse("2001-02-01T00:00:00.00Z"))
                          .isInSameYearAs(Instant.parse("2001-01-01T00:00:00.00Z"))
                          .isNotIn(dateTimeWithMs.toInstant().minusMillis(10), dateTimeWithMs.toInstant().plusMillis(10));

Add float/double finite or infinite assertions

Examples:

assertThat(1.0f).isFinite();
assertThat(Float.NEGATIVE_INFINITY).isInfinite();

assertThat(1.0).isFinite();
assertThat(Double.POSITIVE_INFINITY).isInfinite();

Add doesNotHaveSameHashCodeAs assertion

Verifies that the actual object does not have the same hashCode as the given object.

Examples:

// assertions succeed
assertThat(42L).doesNotHaveSameHashCodeAs(2501L);
assertThat("The Force").doesNotHaveSameHashCodeAs("Awakens");

// assertions fail
assertThat(42L).doesNotHaveSameHashCodeAs(42L);
assertThat("The Force").doesNotHaveSameHashCodeAs("The Force");
assertThat(new Jedi("Yoda", "Blue")).doesNotHaveSameHashCodeAs(new Jedi("Yoda", "Blue"));

Add doesNotHaveToString assertion

Verifies that the actual object does not have the same toString as the given object.

Examples:

CartoonCharacter homer = new CartoonCharacter("Homer");

// Instead of writing ...
assertThat(homer.toString()).isNotEqualTo("Marge");
// ... you can simply write:
assertThat(homer).doesNotHaveToString("Marge");

Add overloaded contains assertions with boxed arrays for primitive array assertions

Primitive array assertions (like long[] assertions) support variants with boxed arrays (e.g. Long[]) for contains assertions.

Examples:

assertThat(new boolean[] { true, false, true }).containsExactly(new Boolean[] {true, false, true });
assertThat(new short[] { 1,  2,  3 }).containsExactly(new Short[] { 1,  2,  3 });
assertThat(new int[] { 1,  2,  3 }).containsExactly(new Integer[] { 1,  2,  3 });
assertThat(new long[] { 1L, 2L, 3L }).contains(new Long[] { 1L, 2L });
assertThat(new float[] { 1.0f, 2.0f, 3.0f }).containsExactly(new Float[] { 1.0f, 2.0f, 3.0f });
assertThat(new double[] { 1.0, 2.0, 3.0 }).containsExactly(new Double[] { 1.0, 1.98, 3.01 }, withPrecision(0.05));
assertThat(new char[] { 'a', 'b', 'c' }).contains(new Character[] { 'a', 'b' });

Add hasOnlyFields assertion

Verifies that the actual object has only the specified fields and nothing else.

The assertion only checks declared fields (inherited fields are not checked) that are not static or synthetic.

Examples:

public class TolkienCharacter {

  private String name;
  public int age;

  public String getName() {
    return this.name;
  }
}

TolkienCharacter frodo = new TolkienCharacter("Frodo", 33);

// assertion succeeds:
assertThat(frodo).hasOnlyFields("name", "age");

// assertions fail:
assertThat(frodo).hasOnlyFields("name");
assertThat(frodo).hasOnlyFields("not_exists");

Allow to pass a lazy description only evaluated when the assertion fails

Lazily specifies the description of the assertion that is going to be called, the given description is not evaluated if the assertion succeeds. This is useful if descriptions are expansive to build.

Examples:

// we all know Mr Frodo is 33 years old
frodo.setAge(33);

// the lazy test description is not evaluated as the assertion succeeds
assertThat(frodo.getAge()).as(() -> "check Frodo's age").isEqualTo(33);

// the lazy test description is evaluated as the assertion fails
assertThat(frodo.getAge()).as(() -> "check Frodo's age").isEqualTo(50);

The error message of the failing assertion is:

[check Frodo's age]
expected: 33
but was : 50

Add isEmptyFile and isNotEmptyFile to Path assertions

Verify that the actual Path is respectively an empty or non empty regular file.

Note that the actual Path must exist and be a regular file.

Examples given the files below:

/root/sub-dir-1/file-1.ext (no content)
/root/sub-dir-1/file-2.ext (content)

Here are some assertions examples:

Path withoutContent = Paths.get("/root/sub-dir-1/file-1.ext");
Path withContent = Paths.get("/root/sub-dir-1/file-2.ext");

// The following assertions succeed:
assertThat(withoutContent).isEmptyFile();
assertThat(withContent).isNotEmptyFile();

// The following assertions fail:
assertThat(withoutContent).isNotEmptyFile();
assertThat(withContent).isEmptyFile();

AssertJ Core 3.18.1

Release date: 2020-11-11

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio and Kim S. Ly.

Binary compatibility

The release is not binary compatible with the previous minor version.

New features

  • Add map and flatMap to iterable assertions as aliases of extracting and flatExtracting.

Improvements

  • Make isElementOfCustomAssert protected to ease integration with 3rd party libraries like XML Unit.

  • Add Javadoc for methods that needs to be implemented in AbstractIterableAssert subclasses.

  • Internal: Adjust hasPackage tests style. (Stefano Cordio)

  • Internal: Bump junit-jupiter version from 5.6.2 to 5.6.3. (Erhard Pointl)

  • Internal: Bump mockito from 3.5.15 to 3.6.0. (Erhard Pointl)

  • Internal: Bump byte-buddy version from 1.10.17 to 1.10.18.

Fixed

  • Fix ShouldNotHaveSameClass error message. ( Kim S. Ly)

  • Fix hasNotFailed Javadoc deprecation example.

AssertJ Core 3.18.0

Release date: 2020-10-25

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, Pascal Schumacher, Fr Jeremy Krieg, BJ Hargrave, Matteo Mirk and Valeriy Vyrva.

Shout out to Fr Jeremy Krieg for adding soft assertions field injection capability in JUnit 5 SoftAssertionsExtension!

Binary compatibility

The release is not binary compatible with the previous minor version.

Breaking changes

  • Breaking change: disable bare name getter by default, to get the previous behaviour back, call Assertions.setExtractBareNamePropertyMethods(true);

  • Exclude net.bytebuddy.experimental property from shading scope: ensures that the property controls ByteBuddy experimental features also inside AssertJ, usually needed when using JDK early access builds. Before this change, ByteBuddy experimental features in AssertJ could only be controlled by org.assertj.core.internal.bytebuddy.experimental.

New features

Improvements

  • Add AssertionError factory methods to AbstractAssert. (Fr Jeremy Krieg)

  • Improve describe error readability of ElementsShouldSatisfy. (Drummond Dawson)

  • Improve isTrue/isFalse assertions error messages.

  • Use two spaces indentation in "should be close to" error messages.

  • Internal: Add CodeQL analysis. (Stefano Cordio)

  • Internal: Clean up some pom dependencies now that Bnd 5.2.0 is being used (BJ Hargrave)

  • Internal: Add assumptions it tests. (Stefano Cordio)

  • Internal: Reduce visibility of FieldUtils as no method is publicly accessible. (Stefano Cordio)

  • Internal: Remove unnecessary semicolons. (Erhard Pointl)

  • Internal: Enforce surefire encoding. (Stefano Cordio)

  • Internal: Bump jackson-databind from 2.11.2 to 2.11.3.

  • Internal: Bump org.eclipse.osgi from 3.15.300 to 3.16.0.

  • Internal: Bump equalsverifier from 3.4.2 to 3.5.

  • Internal: Bump byte-buddy from 1.10.14 to 1.10.17.

  • Internal: Bump assertj-parent-pom from 2.2.7 to 2.2.8.

  • Internal: Bump mockito from 3.5.10 to 3.5.15.

  • Internal: Bump guava from 29.0-jre to 30.0-jre.

  • Internal: Bump jacoco-maven-plugin from 0.8.5 to 0.8.6. (Stefano Cordio)

  • Internal: Bump bnd from 5.1.2 to 5.2.0.

Fixed

  • Honor toString if overridden in Iterable that are not collections.

Deprecated

  • Deprecate isXmlEqualTo in favor of XML Unit.

  • Deprecate SoftlyExtension in favor of SoftAssertionsExtension that now supports field injection.

  • Deprecate hasFailed() CompletableFuture assertion:

// deprecated
assertThat(future).hasFailed();
// instead calls
assertThat(future).isCompletedExceptionally()
                  .isNotCancelled();
  • Deprecate hasFailedWithThrowableThat() CompletableFuture assertion in favor failsWithin:

// deprecated
CompletableFuture future = new CompletableFuture();
future.completeExceptionally(new RuntimeException("boom!"));

assertThat(future).hasFailedWithThrowableThat().isInstanceOf(RuntimeException.class);
                                               .hasMessage("boom!");
// instead calls
assertThat(future).failsWithin(1, TimeUnit.SECONDS)
                  .withThrowableOfType(RuntimeException.class)
                  .withMessage("boom!");

Please note that the semantics of a failure in failsWithin differs from hasFailedWithThrowableThat, failsWithin tries to get the future after the given duration and return the exception that led to its failure while hasFailedWithThrowableThat checks the future is completed exceptionnaly and was not cancelled.

  • Deprecate hasNotFailed() CompletableFuture assertion:

CompletableFuture future = new CompletableFuture();
future.cancel(true);
// deprecated
assertThat(future).hasNotFailed();
// instead calls
assertThat(future).matches (f -> f.isNotCompletedExceptionally() || f.isCancelled());

Add hasPackage to Class assertions

Verifies that the actual Class under test has the given package name.

Example:

package one.two;

class MyClass {}

assertThat(MyClass.class).hasPackage("one.two")
                         .hasPackage(Package.getPackage("one.two"));

Add isCloseTo to java.time.Duration assertions

Verifies that the actual Duration is close to the given one within the given allowed difference (assertion succeeds if difference = allowed difference).

This is equivalent of: abs(actual - expected) ≤ allowed difference.

For readability, Assertions.withMarginOf(Duration) can be used to express the allowed difference.

Examples:

Duration twoMinutes = Duration.ofMinutes(2);

assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), Duration.ofMinutes(5));
assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(-3), Duration.ofMinutes(10));
assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), Duration.ofMinutes(1));

// assertions using withMarginOf syntactic sugar
assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), withMarginOf(Duration.ofMinutes(5)));
assertThat(twoMinutes).isCloseTo(Duration.ofMinutes(3), withMarginOf(Duration.ofMinutes(1)));

Add failsWithin to Future and CompletableFuture assertionss

Checks that the Future/CompletableFuture does not complete within the given time and returns the exception that caused the failure for further (exception) assertions, the exception can be any of InterruptedException, ExecutionException, TimeoutException or CancellationException as per Future.get(long, TimeUnit) behaviour.

WARNING

failsWithin does not fully integrate with soft assertions, if the future completes the test will fail immediately (the error is not collected as a soft assertion error), if the assertion succeeds the chained assertions are executed and any errors will be collected as a soft assertion errors. The rationale is that if we collect failsWithin error as a soft assertion error, the chained assertions would be executed but that does not make sense since there is no exception to check as the future has completed.

Examples:

ExecutorService executorService = Executors.newSingleThreadExecutor();

Future<String> future = executorService.submit(() -> {
  Thread.sleep(100);
  return "ook!";
});

// assertion succeeds as the future is not completed after 50ms
assertThat(future).failsWithin(Duration.ofMillis(10))
                  .withThrowableOfType(TimeoutException.class);

// alternative assertion syntax, the time out is expressed with a TimeUnit value
assertThat(future).failsWithin(10, TimeUnit.MILLISECONDS);


// fails as the future is completed within 200ms
assertThat(future).failsWithin(Duration.ofMillis(200));

Improve isTrue/isFalse assertions error messages

Before:

Expecting:
 <false>
to be equal to:
 <true>
but was not.

After:

Expecting value to be true but was false

Add hasValueSatisfying to AtomicReference assertions

Verifies that the atomic under test has a value satisfying the given predicate.

Examples:

assertThat(new AtomicReference("foo")).hasValueMatching(result -> result != null);

// you can pass a description for the predicate that will be used in the error message
assertThat(new AtomicReference("foo")).hasValueMatching(result -> result != null, "expected not null");

Add hasValueSatisfying to AtomicReference assertions

Verifies that the atomic under test has a value satisfying the given requirements.

Example:

assertThat(new AtomicReference("foo")).hasValueSatisfying(result -> assertThat(result).isNotBlank());

AssertJ Core 3.17.2

Release date: 2020-09-06

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio and BJ Hargrave.

Binary compatibility

The release is not binary compatible with the previous minor version.

Breaking changes

  • Change precedence of assumption classes when raising the assumption exception:

    • Old order: org.opentest4j.TestAbortedException, org.testng.SkipException and org.junit.AssumptionViolatedException

    • New order: org.testng.SkipException, org.junit.AssumptionViolatedException and org.opentest4j.TestAbortedException

Improvements

  • Handle soft proxies for custom assert classes in OSGi bundles. (BJ Hargrave)

  • Internal: Add OSGi integration tests. (BJ Hargrave)

  • Internal: Fix maven-shade-plugin warning about overlapping MANIFEST.MF. (Stefano Cordio)

  • Internal: Exclude net.bytebuddy.experimental from shading scope. (Stefano Cordio)

  • Internal: Bump mockito version from 3.5.5 to 3.5.10. (Erhard Pointl, Stefano Cordio)

Fixed

  • Fix: assumptions were broken in JUnit4 when opentest4j is in the classpath. (Stefano Cordio)

AssertJ Core 3.17.1

Release date: 2020-08-30

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, Pascal Schumacher, rpolton, Andrey Nudko and Davide Angelocola.

Binary compatibility

The release is not binary compatible with the previous minor version.

Breaking changes

  • Change precedence of assumption classes when raising the assumption exception:

    • Old order: org.junit.AssumptionViolatedException, org.opentest4j.TestAbortedException and org.testng.SkipException

    • New order: org.opentest4j.TestAbortedException, org.testng.SkipException and org.junit.AssumptionViolatedException

Improvements

  • Speed up recursive comparison by caching results of Method lookup performed by reflection and using conditions instead of catching exceptions. (Andrey Nudko)

  • Make contains and containsOnly assertions to work for iterables that can be traversed only once. (rpolton)

  • Minor code cleanup. (Davide Angelocola)

  • Internal: Add Java 16 EA build. (Stefano Cordio)

Fixed

  • Fix Object2DArrayAssert#isDeepEqualTo(). (Stefano Cordio)

AssertJ Core 3.17.0

Release date: 2020-08-23

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, Pascal Schumacher, Junhao Liang, Bibibiu, Andrey "Bass" Shcheglov, SuntCrick, Yubin Hu, rpolton, Ting Sun, Peng Weiyuan, Brummolix, Maciej Wajcht, Hayden Meloche, Cal027, sullis, mgrafl, Rupert Madden-Abbott, Shiva, Ahmad M, Phillip Webb and BJ Hargrave.

Joel Costigliola: Apologies to Hayden Meloche, when integrating his work the final squashed commit was pushed with me as the author instead of him, sorry Hayden!

Binary compatibility

The release is not binary compatible with the previous minor version.

Breaking changes

  • Make the recursive comparison to ignore all overridden equals by default as most users were confused by it, call usingOverriddenEquals to revert to the previous behaviour.

  • Improve AssertionFailedError by replacing actual and expected by their representation as some default toString are not great (arrays for example).

  • Make Offset, MapEntry, Percentage, FieldLocation and Index final.

  • Add 2D array assertions, which provide new and potentially different methods for existing array assertions having two-dimensional arguments.

New features

Improvements

  • Allow specifying equals BiPredicate instead of Comparator in recursive comparison.

  • Display the beginning and the end of huge iterables/array/map instead of just the beginning. (SuntCrick)

  • Handle infinite or singly-traversable iterables for sequences assertions. (rpolton) - see #1938 for details.

  • Explicitly set Javadoc locale to English. (Erhard Pointl)

  • Make Percentage and FieldLocation final, update equals() and hashCode(). (Ahmad M)

  • Make Offset, MapEntry and Index final, update equals() and hashCode(). (Stefano Cordio)

  • Javadoc improvements. (Stefano Cordio, Erhard Pointl)

  • Better handle soft proxies for custom assert classes in OSGi bundles. (BJ Hargrave)

  • Best effort to avoid cycles when representing iterables or object arrays.

  • Internal: Refactor urls test classes. (SuntCrick)

  • Internal: Add tests for hasNoPath URL/URI assertions. (Ting Sun)

  • Internal: Bump equalsverifier from 3.2 to 3.4.2.

  • Internal: Bump commons-io from 2.6 to 2.7.

  • Internal: Bump commons-lang3 from 3.10 to 3.11.

  • Internal: Bump bnd version from 5.0.1 to 5.1.2 and re-enable java 15 build. (Pascal Schumacher)

  • Internal: Bump maven-shade-plugin from 3.2.3 to 3.2.4.

  • Internal: Bump byte-buddy version from 1.10.10 to 1.10.14.

  • Internal: Bump mockito version from 3.3.3 to 3.5.5 (Erhard Pointl)

  • Internal: Add dependabot. (sullis)

  • Internal: Bump actions/cache from v1 to v2.

  • Internal: Fix "Remove this 'public' modifier" sonar violations. (Erhard Pointl, Shiva)

  • Internal: Make Longs_assertIsOne_Test#should_succeed_since_actual_is_one testing assertIsOne. (Erhard Pointl)

  • Internal: Make IterableAssert_anyMatch_Test testing anyMatch and IterableAssert_anySatisfy_Test testing anySatisfy. (Erhard Pointl)

  • Internal: Add missing ObjectArrayAssert_anySatisfy_Test test. (Erhard Pointl)

  • Internal: Fix mockito warnings. (Erhard Pointl)

  • Internal: Disable shallow clone during Sonar analysis. (Stefano Cordio)

  • Internal: Clean up code. (Stefano Cordio, Erhard Pointl)

Fixed

  • AbstractByteArrayAssert#asString now build a new String with the default or a given charset instead returning the byte array toString.

  • Allow soft assertion failures to be recorded from multiple threads. (Andrey "Bass" Shcheglov)

  • Fix containsExactly assertion that failed for iterable that can only be read once. (rpolton)

  • Fix references to main branch after master was renamed to main to support Black Lives Matter. (Steven Crockett)

  • Fix Recursive comparison that did not compare enums as they don’t have fields, now it compares enums with equals.

Deprecated

  • hasOnlyOneElementSatisfying(Consumer<? super ELEMENT> elementAssertions) has been deprecated in favor of singleElement()

  • Deprecate all non recursive field by field comparison assertions in favor of the recursive comparison ones:

    • isEqualToComparingFieldByField

    • isEqualToIgnoringNullFields

    • isEqualToComparingOnlyGivenFields

  • Deprecate AssertionErrorMessagesAggregrator in favor of AssertionErrorMessagesAggregator. (Ahmad M)

Add java.time.Period assertions

Provides the following assertions for the Period:

  • hasDays(int expectedDays): Verifies that the actual Period has the given days.

  • hasMonths(int expectedMonths): Verifies that the actual Period has the given months.

  • hasYears(int expectedYears): Verifies that the actual Period has the given years.

  • isNegative(): Verifies that the actual Period is negative (i.e. is less than Period.ZERO).

  • isPositive(): Verifies that the actual Period is positive (i.e. is greater than Period.ZERO).

Examples:

assertThat(Period.ofYears(5)).hasYears(5);

assertThat(Period.ofMonths(5)).hasMonths(5);

assertThat(Period.ofDays(5)).hasDays(5)
                            .isPositive();

assertThat(Period.ofMonths(-5)).isNegative();

Display the beginning and the end of huge iterables/array/map

Before this version only the first maxElementsForPrinting elements would be displayed, now the maxElementsForPrinting displayed elements are split between first and last elements.

The number of elements to display can be set with Assertions.setMaxElementsForPrinting(n);

Example:

// 6 elements array
String[] greatBooks = {"A Game of Thrones", "The Lord of the Rings", "Assassin's Apprentice",
                       "Guards! Guards!", "The Lies of Locke Lamora", "Aux Ombres d’Abyme"};

// limit the number of elements to display/print to 4
Assertions.setMaxElementsForPrinting(4);

// formatted as:
["A Game of Thrones", "The Lord of the Rings", ... "The Lies of Locke Lamora", "Aux Ombres d’Abyme"]

Add isEmpty to InputStream assertion

Verifies whether the content of the actual InputStream is empty.

Examples:

// assertion will pass
assertThat(new ByteArrayInputStream(new byte[] {})).isEmpty());

// assertions will fail
assertThat(new ByteArrayInputStream(new byte[] {0xa})).isEmpty();

Add isNotEmpty to InputStream assertion

Verifies that the content of the actual InputStream is not empty.

Examples:

// assertion will pass
assertThat(new ByteArrayInputStream(new byte[] {0xa})).isNotEmpty());

// assertions will fail
assertThat(new ByteArrayInputStream(new byte[] {})).isNotEmpty();

Add doesNotContainIgnoringCase to CharSequence assertions

Verifies that the actual CharSequence does not contain any of the given values, ignoring case considerations.

Example:

// assertions will pass
assertThat("Frodo").doesNotContainIgnoringCase("pippin")
                   .doesNotContainIgnoringCase("Merry", "sam");

// assertions will fail
assertThat("Frodo").doesNotContainIgnoringCase("Fro", "Gimli", "Legolas");
assertThat("Frodo").doesNotContainIgnoringCase("fro");

primitive and Object 2D array assertions

The following assertions are available 2D arrays, here they are for int[][]:

  • isEqualTo(Object expected): Verifies that the actual int[][] is equal to the given one.

  • isDeepEqualTo(int[][] expected): Verifies that the actual 2D array is deeply equal to the given one.

  • isEmpty(): Verifies that the actual array is empty, i.e the array has no elements, said otherwise it can have any number of rows but all rows must be empty.

  • isNullOrEmpty(): Verifies that the actual array is null or empty, empty means the same as isEmpty().

  • isNotEmpty(): Verifies that the actual array is not empty, i.e. the array has at least one element.

  • contains(int[] value, Index index): Verifies that the actual array contains the given int[] at the given index.

  • doesNotContain(int[] value, Index index): Verifies that the actual array does not contain the given value at the given index.

  • hasDimensions(int expectedFirstDimension, int expectedSecondDimension): Verifies that the actual 2D array has the given dimensions.

  • hasSameDimensionsAs(Object array): Verifies that the actual int[][] has the same dimensions as the given array.

The same assertions are available for long[][], short[][], byte[][], float[][], double[][], boolean[][] and Object[][] (obviously replacing int by the array type).

Object[][] assertions are generic, they take a parameter type, ex: isDeepEqualTo(ELEMENT[][] expected).

The migration path from one-dimensional array assertions is the following:

Before 3.17.0 After 3.17.0

contains(ELEMENT, Index)

contains(ELEMENT[], Index)

doesNotContain(ELEMENT, Index)

doesNotContain(ELEMENT[], Index)

hasSameSizeAs(Iterable)

hasSameDimensionsAs(Object)

hasSize(int)

hasDimensions(int, int)

isEmpty()

isEmpty()

isEqualTo(Object)

isEqualTo(Object)

isNotEmpty()

isNotEmpty()

isNullOrEmpty()

isNullOrEmpty()

The remaining one-dimensional array assertions do not have a direct replacement. Please raise a feature request if there is any use case for them.

Add isEven/isOdd assertions for byte, short, int and long

Verifies whether the given byte, short, int and or long is even/odd.

Examples:

assertThat(12).isEven();
assertThat(-46).isEven();

assertThat(3).isOdd();
assertThat(-17).isOdd();

Add singleElement to iterable assertions

singleElement() and singleElement(InstanceOfAssertFactory assertFactory) verify that the Iterable under test contains a single element and allow to perform assertions on that element (this is a shorthand for hasSize(1).first()).

You can only chain Object assertions after singleElement(), to get strongly typed assertions, use singleElement(InstanceOfAssertFactory) and pass the proper InstanceOfAssertFactory.

Examples:

import static org.assertj.core.api.InstanceOfAssertFactories.STRING;
import static org.assertj.core.api.Assertions.as; // syntactic sugar

List<String> babySimpsons = list("Maggie");

// assertion succeeds, only Object assertions are available after singleElement()
assertThat(babySimpsons).singleElement()
                        .isEqualTo("Maggie");

// String assertion succeeds, String assertions as we have passed InstanceOfAssertFactories.STRING
assertThat(babySimpsons).singleElement(as(STRING))
                        .startsWith("Mag");

Add succeedsWithin to Future and CompletableFuture assertions

succeedsWithin waits if necessary for at most the given time for this future to complete and then returns its result for further assertions.

If the future’s result is not available for any reason an assertion error is thrown.

To get assertions for the future result’s type use the method taking an additionnal InstanceOfAssertFactory parameter instead.

Examples:

ExecutorService executorService = Executors.newSingleThreadExecutor();

Future<String> future = executorService.submit(() -> {
  Thread.sleep(100);
  return "ook!";
});

// assertion succeeds
assertThat(future).succeedsWithin(200, TimeUnit.MILLISECONDS)
                  .isEqualTo("ook!");

// same assertion with a Duration to express the time out
Duration timeout = Duration.ofMillis(200);
assertThat(future).succeedsWithin(timeout)
                  .isEqualTo("ook!");

// fails as the future is not done after the given timeout
assertThat(future).succeedsWithin(50, TimeUnit.MILLISECONDS);

Examples with stronly typed assertions and CompletableFuture:

import static org.assertj.core.api.InstanceOfAssertFactories.STRING;
import static org.assertj.core.api.Assertions.as; // syntactic sugar

CompletableFuture<String> future = CompletableFuture.completedFuture("ook!");

Duration timeout = Duration.ofMillis(200);

// strongly typed assertion:
assertThat(future).succeedsWithin(timeout, as(STRING))
                  .contains("ok");

// same assertion with the timeout expressed differently:
assertThat(future).succeedsWithin(200, TimeUnit.MILLISECONDS, as(STRING))
                  .contains("ok");

Add filteredOn(Function, expectedValue) to Iterable, Object[] and AtomicReferenceArray assertions

Filters the iterable under test keeping only elements for which the result of the function is equal to expectedValue.

It allows to filter elements more safely than by using filteredOn(String, Object) as it doesn’t utilize introspection.

As an example, let’s check all employees 800 years old (yes, special employees): Examples:

Employee yoda   = new Employee(1L, new Name("Yoda"), 800);
Employee obiwan = new Employee(2L, new Name("Obiwan"), 800);
Employee luke   = new Employee(3L, new Name("Luke", "Skywalker"), 26);
Employee noname = new Employee(4L, null, 50);

List<Employee> employees = newArrayList(yoda, luke, obiwan, noname);

assertThat(employees).filteredOn(Employee::getAge, 800)
                     .containsOnly(yoda, obiwan);

assertThat(employees).filteredOn(e -> e.getName(), null)
                     .containsOnly(noname);

AssertJ Core 3.16.1

Release date: 2020-05-09

Contributors

Thanks to Erhard Pointl and Eddú Meléndez Gonzales for their contributions.

Fixed

  • Fix NPE in recursive comparison when checking local or anonymous classes. (#1868)

  • Fix assertThat(Duration actual) Javadoc. (Eddú Meléndez Gonzales)

AssertJ Core 3.16.0

Release date: 2020-05-05

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, Pascal Schumacher, Wim Deblauwe, Fabien Duminy, Piotrek Żygieło, Indrek Priks, Jakzi666, Daniel Avila, Harisha Talanki, Grzegorz Piwowarek, Andreas Mager, Sunt-ing, ebundy, Stefan Birkner, WuYff, Cal027, Yubin Hu and Fr Jeremy Krieg.

Breaking changes

  • AbstractSoftAssertions is now abstract

  • The following base soft assertions classes were changed to interfaces (with default methods) and renamed:

    • AbstractBDDSoftAssertions was renamed to BDDSoftAssertionsProvider

    • AbstractStandardSoftAssertions was renamed to StandardSoftAssertionsProvider

    • Java6AbstractBDDSoftAssertions was renamed to Java6BDDSoftAssertionsProvider

    • Java6AbstractStandardSoftAssertions was renamed to Java6StandardSoftAssertionsProvider

  • Move ThrowingCallable from AbstractSoftAssertions to SoftAssertionsProvider.

New features

Improvements

  • ByteArrayAssert.containsExactly(byte…​) error message now mentions not found and unexpected elements. (Indrek Priks)

  • In "should be package private" class assertion, the error message now explicitly mentions package-private instead of a blank value.

  • Use primitive comparison in Float and Double isNotEqualTo when compared to primitive float/double values.

  • Disambiguate colliding date/time representation.

  • Support up to four arguments for satisfiesAnyOf(). (Jakzi666)

  • Clarify the error message when comparing float/double NaN with ==.

  • Use a more descriptive element’s name in ShouldContain/ShouldContainOnly error message. (WuYff)

  • Add short array assertions taking int…​. (Daniel Avila)

  • Use AssertJ site theme for Javadoc.

  • Improve converting JUnit/JUnit5 assertions to AssertJ. (Yubin Hu)

  • Move core extracting features from AbstractObjectAssert to AbstractAssert, making them available for custom assertions. (Stefano Cordio)

  • Improve line number accuracy in soft assertion error messages. (Stefano Cordio)

  • Internal: introduce EqualsVerifier for internal tests. (Stefano Cordio)

  • Internal: optimize Charset finding in tests. (Fabien Duminy)

  • Internal: clean up unused imports (Erhard Pointl, Piotrek Żygieło, Stefano Cordio, Pascal Schumacher)

  • Internal: use static imports. (Piotrek Żygieło)

  • Internal: remove unnecessary type parameters from extractors. (Stefano Cordio)

  • Internal: access assertion info directly in AtomicLongAssert/AtomicIntegerAssert. (Grzegorz Piwowarek)

  • Re-enable Sonar reports. (Stefano Cordio)

  • Update ByteBuddy to version 1.10.10.

  • Update JUnit Jupiter to version 5.6.2 (still optional).

Fixed

  • Fix infinite recursion in recursive comparison when dealing with Path. (#1855)

  • Fix recursive comparison way of tracking already visited objects. (#1854)

  • Fix typos (Wim Deblauwe, Stefano Cordio)

Deprecated

  • Deprecate areEqual() and areEqualArrays() in org.assertj.core.util.Objects.

  • Deprecate temporaryFolder() in org.assertj.core.util.Files. (Sunt-ing)

Add java.util.concurrent.atomic.LongAdder assertions

The following java.util.concurrent.atomic.LongAdder assertions are available:

  • hasValue(long expected), which verifies that the actual LongAdder sum has the given value.

  • doesNotHaveValue(long unexpected), which verifies that the actual LongAdder sum has not the given value.

  • All the assertions provided by NumberAssert, using the LongAdder sum as actual value.

  • All the assertions provided by ComparableAssert, using the LongAdder sum as actual value.

Comparison ignoring punctuation and normalizing whitespaces

Verifies that the actual CharSequence is equal to the given one, after the punctuation of both strings have been normalized.

To be exact, the following rules are applied:

  • All punctuation of actual and expected strings are ignored and whitespaces are normalized

  • Punctuation is any of the following characters: !"#$%&'()*+,-./:;=<>?@[\]^_{|}~\`

Examples:

// assertions succeed:
assertThat("Game'of'Thrones").isEqualToNormalizingPunctuationAndWhitespace("GameofThrones")
assertThat("Game of Throne's").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
assertThat(":Game of Thrones:").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
assertThat(":Game-of-Thrones:").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
assertThat("Game of Thrones?").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
assertThat("Game of Thrones!!!").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
assertThat("Game of  {{(!)}}    Thrones!!!").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones")
assertThat("{(Game)-(of)-(Thrones)!!!}").isEqualToNormalizingPunctuationAndWhitespace("GameofThrones");

// assertions fail:
assertThat("Game-of-Thrones").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones");
assertThat("{Game:of:Thrones}").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones");
assertThat("{(Game)-(of)-(Thrones)!!!}").isEqualToNormalizingPunctuationAndWhitespace("Game of Thrones");

isBase64

Verifies that the given String is a valid Base64 encoded string. (this is not available for CharSequence).

Examples:

// assertions succeeds
assertThat("QXNzZXJ0Sg==").isBase64();

// assertion succeeds even without padding as it is optional by specification
assertThat("QXNzZXJ0Sg").isBase64();

// assertion fails as it has invalid Base64 characters
assertThat("inv@lid").isBase64();

decodedAsBase64

Decodes the actual String value as a Base64 encoded string, the decoded bytes becoming the new array under test.

Examples:

// assertions succeeds
assertThat("QXNzZXJ0Sg==").decodedAsBase64()
                          .containsExactly("AssertJ".getBytes());

// assertion succeeds even without padding as it is optional by specification
assertThat("QXNzZXJ0Sg").decodedAsBase64()
                        .containsExactly("AssertJ".getBytes());

// assertion fails as it has invalid Base64 characters
assertThat("inv@lid").decodedAsBase64();

Add asHexString to byte[] assertions

Converts the actual byte array under test to an hexadecimal String and returns assertions for the computed String allowing String specific assertions from this call.
The hexadecimal String representation is in upper case.

Example :

byte[] bytes = new byte[] { -1, 0, 1 };

// assertion will pass
assertThat(bytes).asHexString()
                 .startsWith("FF")
                 .isEqualTo("FF0001");

Add isEqualToWithSortedQueryParameters] to URL assertions

Verifies that the actual URL is equivalent to the given one after their parameters are sorted.

Example :

URL url = new URL("http://example.com?a=b&c=d");

// these assertions succeed ...
assertThat(url).isEqualToWithSortedQueryParameters(new URL("http://example.com?c=d&a=b"))
               .isEqualToWithSortedQueryParameters(new URL("http://example.com?a=b&c=d"));

// ... but this one fails as parameters do not match.
assertThat(url).isEqualToWithSortedQueryParameters(new URL("http://example.com?a=b&c=e"));

//... and this one fails as domains are different.
assertThat(url).isEqualToWithSortedQueryParameters(new URL("http://example2.com?amp;a=b&c=d"));

Add isDirectoryRecursivelyContaining to File/Path assertions

Verify that the actual File/Path directory or any of its subdirectories (recursively) contains at least one file matching the given criteria expressed as:

  • a String interpreted as a path matcher (as per FileSystem.getPathMatcher(String))

  • a String interpreted as a path matcher (as per FileSystem.getPathMatcher(String))

These assertions are similart to isDirectoryContaining but recursively go into subdirectories.

Note that the actual File/Path must exist and be a directory.

Examples with files given the following directory structure:

root
|—— foo
|    |—— foobar
|         |—— foo-file-1.ext
|—— foo-file-2.ext

Examples with syntax patterns:

File root = new File("root");

// The following assertions succeed:
assertThat(root).isDirectoryRecursivelyContaining("glob:**foo")
                .isDirectoryRecursivelyContaining("glob:**ooba*")
                .isDirectoryRecursivelyContaining("glob:**file-1.ext")
                .isDirectoryRecursivelyContaining("regex:.*file-2.*")
                .isDirectoryRecursivelyContaining("glob:**.{ext,dummy}");

// The following assertions fail:
assertThat(root).isDirectoryRecursivelyContaining("glob:**fooba");
assertThat(root).isDirectoryRecursivelyContaining("glob:**.bin");
assertThat(root).isDirectoryRecursivelyContaining("glob:**.{java,class}");

Examples with predicates:

File root = new File("root");

// The following assertions succeed:
assertThat(root).isDirectoryRecursivelyContaining(file -> file.getName().startsWith("foo-file-1"))
                .isDirectoryRecursivelyContaining(file -> file.getName().endsWith("file-2.ext"))
                .isDirectoryRecursivelyContaining(file -> file.getName().equals("foo"))
                .isDirectoryRecursivelyContaining(file -> file.getParentFile().getName().equals("foo"))

// The following assertions fail:
assertThat(root).isDirectoryRecursivelyContaining(file -> file.getName().equals("foo-file-1"))
assertThat(root).isDirectoryRecursivelyContaining(file -> file.getName().equals("foo/foobar"));

Add hasBinaryContent to InputStream assertions

Verifies that the binary content of the actual InputStream is exactly equal to the given one.

Example: the following failing assertion …​

InputStream inputStream = new ByteArrayInputStream(new byte[] {1, 2});

// assertion will pass
assertThat(inputStream).hasBinaryContent(new byte[] {1, 2});

// assertions will fail
assertThat(inputStream).hasBinaryContent(new byte[] { });
assertThat(inputStream).hasBinaryContent(new byte[] {0, 0});

Add containsOnlyOnceElementsOf to Iterable/Object array/AtomicReferenceArray assertions

Verifies that the actual group contains the elements of the given iterable only once (same semantic as containsOnlyOnce(Object…​)).

Examples:

// assertions will pass
assertThat(list("winter", "is", "coming")).containsOnlyOnceElementsOf(list("winter"))
                                          .containsOnlyOnceElementsOf(list("coming", "winter"));

// assertions will fail
assertThat(list("winter", "is", "coming")).containsOnlyOnceElementsOf(list("Lannister"));
assertThat(list("Arya", "Stark", "daughter", "of", "Ned", "Stark")).containsOnlyOnceElementsOf(list("Stark"));
assertThat(list("Arya", "Stark", "daughter", "of", "Ned", "Stark")).containsOnlyOnceElementsOf(list("Stark", "Lannister", "Arya"));

Disambiguate colliding date/time representation

Different date/time types can be represented the same way (LocalDateTime and Date for example) which makes it difficult to understand error messages as they don’t show any difference between actual and expected values. AssertJ now adds the date/time type name for types whose representation may collide.

Example: the following failing assertion …​

Date now = new Date();
Object localDateTime = LocalDateTime.ofInstant(now.toInstant(), ZoneId.systemDefault());

assertThat(List.of(localDateTime)).containsExactly(now);

... fails with this error:

Expecting:
  <[2020-03-19T22:32:42.875 (java.time.LocalDateTime)]>
to contain exactly (and in same order):
  <[2020-03-19T22:32:42.875 (java.util.Date)]>
but some elements were not found:
  <[2020-03-19T22:32:42.875 (java.util.Date)]>
and others were not expected:
  <[2020-03-19T22:32:42.875 (java.time.LocalDateTime)]>

Before that the error would have been confusing:

Expecting:
  <[2020-03-19T22:32:42.875]>
to contain exactly (and in same order):
  <[2020-03-19T22:32:42.875]>
but some elements were not found:
  <[2020-03-19T22:32:42.875]>
and others were not expected:
  <[2020-03-19T22:32:42.875]>

AssertJ Core 3.15.0

Release date: 2020-01-28

The recursive comparison API has been promoted and is not a beta API anymore.

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, Pascal Schumacher, BJ Hargrave, Raymond Augé, Thomas Weißschuh, Maciej Wajcht, Hayden Meloche, Filip Hrisafov, Jayati Goyal, Gyumin Kim, Clemens Grabmann, Roman Leventov, Fr Jeremy Krieg, Benoit Dupont, Nikolaos Georgiou, Christian Stein, Jeremy Landis, Graham Dennis, Fabien Duminy, Tommy Situ and Vincent Ricard.

Shout out to Vincent Ricard for the various tests refactoring, that was quite a lot of work!

Breaking changes

  • Compares OffsetDateTime, ZonedDateTime and LocalDateTime using their timeLineOrder() comparator as default.

    • For OffsetDateTime the timeLineOrder comparator only compares the underlying instant and ignores different timezones / offsets / chronologies.

    • For ZonedDateTime the timeLineOrder comparator ignores the chronology, this is equivalent to comparing the epoch-second and nano-of-second.

    • For LocalDateTime the timeLineOrder comparator ignores the chronology, this is equivalent to comparing the epoch-day and nano-of-day and allows dates in different calendar systems to be compared based on the position of the date-time on the local time-line.

  • A single Path parameter for containsOnlyKeys in Map assertions is treated as a single key rather than an Iterable of keys.

  • Fix floating point comparison behavior in DoubleAssert and FloatAssert, which now follows primitive comparison (==, ≤, ≥) when the expected value is primitive but uses the corresponding equals semantic when the expected value is a wrapper.

  • Fix a double decoding issue in UriAssert, which now uses the raw query to evaluate URI parameters avoiding the mishandling of escaped & and =. (Graham Dennis)

  • Remove duplication for Descriptable implementations using default methods. This is a binary incompatible change. (Fr Jeremy Krieg)

New features

Improvements

  • Show explicitly if a class is package-private in ClassModifierShouldBe error message.

  • Various module descriptor improvements: (Christian Stein and Stefano Cordio)

    • Remove JSR-305 due to issues with java 9 modules. (Stefano Cordio)

    • Remove .core.internal from exported packages. (Stefano Cordio)

  • Update ByteBuddy to version 1.10.6.

  • Update JUnit to version 4.13 (still optional).

  • Update JUnit Jupiter to version 5.6.0 (still optional).

  • Make OSGi import of jdk.* packages optional. (BJ Hargrave)

  • Use bnd 5.0.0 to a) use -noclassforname instruction b) generate most up to date OSGi metadata c) add verification that additional package imports never sneak in. (Raymond Augé)

  • Get rid of unnecessary extra arguments in String.format. (Erhard Pointl)

  • Unify actual and expected formatting in hasToString() error which is now AssertionFailedError to allow visual comparison. (Thomas Weißschuh)

  • Add missing BDD assertions for exception handling (thenExceptionOfType, thenNullPointerException, thenIllegalArgumentException, thenIOException and thenIllegalStateException). (Maciej Wajcht)

  • Rewrite LocalDateAssert, LocalDateTimeAssert, LocalTimeAssert and OffsetDateTimeAssert tests to be more compliant with the contribution guidelines. (Clemens Grabmann)

  • Remove IntelliJ IDEA configuration file for Language Injection as the rules are part of the built-in configuration since IntelliJ IDEA 2019.3. (Stefano Cordio)

  • Improve performance of containsOnly() on very large collections. (Roman Leventov)

  • Configure GitHub Actions for Windows and MacOS. (Filip Hrisafov)

  • Use parameterized tests for assertHasParameter() in URI assertions. (Stefano Cordio)

  • Show the stack trace of the Throwable under test when hasMessageContaining and hasMessageContainingAll fails. (Benoit Dupont)

  • Bump maven wrapper to 0.5.6. (Jeremy Landis)

  • Improve the representation of failed CompletableFuture showing the exception that caused the failure.

  • Use Objects.requireNonNull instead of manually creating NullPointerExceptions. (Pascal Schumacher)

  • Remove unused methods. (Fabien Duminy)

  • Replace try/catch exception assertion with catchThrowable pattern. (Vincent Ricard)

  • Remove failBecauseExpectedAssertionErrorWasNotThrown. (Vincent Ricard)

  • Replace the TestFailures helper class by the catchThrowable pattern. (Vincent Ricard)

  • Update license year to 2020.

Fixed

  • Fix grammatical errors in README.md (Jayati Goyal)

  • Fix allOf(Iterable) and anyOf(Iterable) that no longer tracked descriptions when built with an Iterable<Condition>.

  • Fix typos in Javadoc and comments. (Erhard Pointl)

  • Add abstract modifier for Java6AbstractStandardSoftAssertions. (Stefano Cordio)

  • Fix typo in Javadoc. (Gyumin Kim)

  • Fix how Enum are compared in recursive comparison which now compares them by value.

  • Fix tests failing only on Windows. (Fr Jeremy Krieg)

  • Refactoring: remove useless null check. (Pascal Schumacher)

  • Fix use equals to compare enum names in recursive comparison.

  • Fix how containsOnlyKeys in MapAssert considers a single Path parameter, which is now treated as a single key rather than an Iterable of keys. (Stefano Cordio)

  • Fix the recursive comparison that used to register fields of objects with overridden equals when it should not have to.

  • Fix property and field extraction with Map input, which now tries at first to extract a property or a field by name and only in case of failure uses the input name as a Map key. (Stefano Cordio)

Deprecated

  • Deprecate hasSameContentAs in favor of hasSameTextualContentAs and the new hasSameBinaryContentAs.

  • Deprecate Preconditions#checkNotNull(Object) in favor of Objects.requireNonNull(Object).

  • Deprecate Preconditions#checkNotNull(Object, String) in favor of Objects.requireNonNull(Object, String).

Add java.time.Duration assertions

The following java.time.Duration assertions are available:

  • hasDays(long otherDays): Verifies that the actual Duration has the given days.

  • hasHours(long otherHours): Verifies that the actual Duration has the given hours.

  • hasMillis(long otherMillis): Verifies that the actual Duration has the given millis.

  • hasMinutes(long otherMinutes): Verifies that the actual Duration has the given minutes.

  • hasNanos(long otherNanos): Verifies that the actual Duration has the given nanos.

  • hasSeconds(long otherSeconds): Verifies that the actual Duration has the given seconds.

  • isNegative(): Verifies that the actual Duration is negative (i.e. < Duration.ZERO)

  • isPositive(): Verifies that the actual Duration is positive (i.e. > Duration.ZERO)

  • isZero(): Verifies that the actual Duration is Duration.ZERO.

Examples:

assertThat(Duration.ofDays(5)).hasDays(5);
assertThat(Duration.ofHours(15)).hasHours(15);

assertThat(Duration.ofMinutes(65)).hasMinutes(65);
assertThat(Duration.ofSeconds(250)).hasSeconds(250);

assertThat(Duration.ofMillis(250)).hasMillis(250);
assertThat(Duration.ofNanos(145)).hasNanos(145);

assertThat(Duration.ofHours(5)).isPositive();
assertThat(Duration.ofMinutes(-15)).isNegative();
assertThat(Duration.ZERO).isZero();

Add isPackagePrivate to Class assertions

Verifies that the actual Class is package-private (i.e. has no modifier).

Example:

class MyClass {}

// this assertion succeeds:
assertThat(MyClass.class).isPackagePrivate();

// this assertion fails:
assertThat(String.class).isPackagePrivate();

Add hasSameBinaryContentAs to File/Path assertions

Verifies that the content of the actual file/path is equal to the content of the given one, the comparison is done at the binary level.

Example with Path (works the same with File):

// The first two paths have the same content, the third does not
Path aPath = Files.write(Paths.get("a-file.bin"), new byte[] { 42 });
Path bPath = Files.write(Paths.get("b-file.bin"), new byte[] { 42 });
Path cPath = Files.write(Paths.get("c-file.bin"), new byte[] { 24 });

// The following assertion succeeds:
assertThat(aPath).hasSameBinaryContentAs(bPath);

// The following assertion fails:
assertThat(aPath).hasSameBinaryContent(cPath);

Add succeedsWithin to CompletableFuture assertions

Waits if necessary for at most the given time for this future to complete, and then returns its result for futher assertions. If the future’s result is not available for any reason an assertion error is thrown.

The time to wait for can be expressed with a Duration or a TimeUnit.

To get assertions for the future result’s type use succeedsWithin that takes an additional InstanceOfAssertFactory parameter.

Examples:

CompletableFuture<String> future = CompletableFuture.completedFuture("ook!");

// assertion expressed with TimeUnit
assertThat(future).succeedsWithin(100, TimeUnit.MILLISECONDS)
                  .isEqualTo("ook!");

// same assertion with Duration
assertThat(future).succeedsWithin(Duration.ofMillis(100))
                  .isEqualTo("ook!");

// STRING is a static import of InstanceOfAssertFactories.STRING
// we can then chain String assertions
assertThat(future).succeedsWithin(100, TimeUnit.MILLISECONDS, STRING)
                  .startsWith("oo");

Add hasSuperclass to Class assertions

Verifies that the actual Class has the given superclass.

Example:

// this assertion succeeds:
assertThat(Integer.class).hasSuperclass(Number.class);

// this assertion succeeds as superclass for array classes is Object:
assertThat(Integer[].class).hasSuperclass(Object.class);

// this assertion fails:
assertThat(String.class).hasSuperclass(Number.class);

// this assertion fails as only direct superclass matches:
assertThat(String.class).hasSuperclass(Object.class);

// this assertion fails as interfaces are not superclasses:
assertThat(String.class).hasSuperclass(Comparable.class);

Add hasNoSuperclass to Class assertions

Verifies that the actual Class has no superclass.

Example:

// this assertion succeeds as interfaces have no superclass:
assertThat(Cloneable.class).hasNoSuperclass();

// this assertion succeeds as primitive types have no superclass:
assertThat(Integer.TYPE).hasNoSuperclass();

// this assertion succeeds as void type has no superclass:
assertThat(Void.TYPE).hasNoSuperclass();

// this assertion fails as Integer has Number as superclass:
assertThat(Integer.class).hasNoSuperclass();

Make recursive comparison API directly available to Iterable, Map, Optional and array assertions

Expose the recursive comparison for Iterable, Map, Optional and array assertions without having to cast them to Object as previously (because the API was only available for Object assertions).

At the moment, the only assertion available after in the recursive comparison is isEqualTo, there are plans to provide type specific recursive assertions in future (ex: iterable contains).

The recursive comparison API lets you finely control how to compare instances, please consult the documentation for a detailed guide.

For the following examples we use Person and Doctor, two classes with the same structure:

public class Person {
  String name;
  boolean hasPhd;
}

public class Doctor {
 String name;
 boolean hasPhd;
}

Doctor drSheldon = new Doctor("Sheldon Cooper", true);
Doctor drLeonard = new Doctor("Leonard Hofstadter", true);
Doctor drRaj = new Doctor("Raj Koothrappali", true);

Person sheldon = new Person("Sheldon Cooper", true);
Person leonard = new Person("Leonard Hofstadter", true);
Person raj = new Person("Raj Koothrappali", true);

Iterable example:

List<Doctor> doctors = list(drSheldon, drLeonard, drRaj);
List<Person> people = list(sheldon, leonard, raj);

// assertion succeeds as both lists contains equivalent items in order.
assertThat(doctors).usingRecursiveComparison()
                   .isEqualTo(people);

Array example:

Doctor[] doctors = { drSheldon, drLeonard, drRaj };
Person[] people = { sheldon, leonard, raj };

// assertion succeeds as both lists contains equivalent items in order.
assertThat(doctors).usingRecursiveComparison()
                   .isEqualTo(people);

Map example:

Map<String, Doctor> doctors = mapOf(entry(drSheldon.name, drSheldon),
                                    entry(drLeonard.name, drLeonard),
                                    entry(drRaj.name, drRaj));

Map<String, Person> people = mapOf(entry(sheldon.name, sheldon),
                                   entry(leonard.name, leonard),
                                   entry(raj.name, raj));

// assertion succeeds as both maps contains equivalent items.
assertThat(doctors).usingRecursiveComparison()
                   .isEqualTo(people);

Optional example:

Optional<Doctor> doctor = Optional.of(drSheldon);
Optional<Person> person = Optional.of(sheldon);

// assertion succeeds as both maps contains equivalent items.
assertThat(doctor).usingRecursiveComparison()
                  .isEqualTo(person);

AssertJ Core 3.14.0

Release date: 2019-10-27

Contributors

Thanks to all the contributors of this release: Erhard Pointl, Stefano Cordio, Jonas Berlin, Thami Inaflas, Geoffrey Arthaud, Carter Kozak, Kevin Toublanc, Krishna Chaithanya Ganta, sowmiyamuthuraman, Edgar Asatryan, Oleksii Khomchenko, Gonzalo Müller Bravo, Stephen O’Rourke, Sven Johansson, William Bakker, Rob Spieldenner, Raymond Pressly, Michael Keppler and Clemens Grabmann.

Breaking changes

  • Stop throwing an IllegalArgumentException when isIn and isNotIn are given an empty group of values.

New features

Improvements

  • AssertJ’s Javadoc are now searchable.

  • Use beautiful AssertJ’s site code style for Javadoc :)

  • ObjectAssert.extracting(String…​) learned to extract nested map key field/property. (Sven Johansson)

  • Prettify allOf and anyOf combined conditions description. (Edgar Asatryan)

  • Clearly differentiate top level objects in the new recursive comparison.

  • Show actual’s stack trace in hasRootCause and hasRootCauseMessage to give users more information. (Oleksii Khomchenko)

  • Show actual’s stack trace in hasMessageMatching and hasMessageFindingMatch to give users more information. (Stephen O’Rourke)

  • Update message text in ShouldHaveSameSizeAs to show both actual and expected collections. (Thami Inaflas)

  • Add matching syntactic sugar method to use Hamcrest Matcher as Condition. (Jonas Berlin)

  • Update ByteBuddy to version 1.10.2.

  • Update Hamcrest to version 2.2.

  • Fix Javadoc typos and incorrect references. (Erhard Pointl, Stefano Cordio)

  • Stop throwing an IllegalArgumentException when isIn and isNotIn are given an empty group of values.

  • Expose AbstractAssert.objects to be used by subclasses.

  • Bump opentest4j from 1.1.1 to 1.2.0. (still optional)

  • Improve HamcrestCondition generic type inference. (Carter Kozak)

  • Remove shouldHaveThrown(Assertion.class) used internally. (sowmiyamuthuraman)

  • Replace catchThrowable + isInstanceOf(AssertionError.class) by expectAssertionError (internal use). (Clemens Grabmann)

  • Rewrite CompletableFutureAssert tests with assertThatAssertionErrorIsThrownBy. (internal use). (Clemens Grabmann)

Fixed

  • Fix BDDSoftAssertions.then(URL actual) that just did not work 🤦‍. (Rob Spieldenner)

  • Fix possible MissingFormatArgumentException in ShouldHaveMessage and ShouldContain. (Erhard Pointl)

  • Fix Javadoc search.

  • Fix Javadoc links. (Stefano Cordio)

  • Fix hasSizeBetween() that did not work with strings. (Geoffrey Arthaud)

  • Fix failing soft assertions when combined with asInstanceOf.

  • Fix missing soft assertions proxying for get of OptionalAssert. (Stefano Cordio)

  • Make convert-junit-assertions-to-assertj.sh conversion script work on Windows. (Michael Keppler)

Deprecated

  • Deprecate the confusing containsOnlyElementsOf in favor of isSubsetOf or hasSameElementsAs.

  • Deprecate Map assertions extracting(Object) and extracting(Object…​) in favor of extractingByKey(KEY) and extractingByKeys(KEY…​), respectively. (Stefano Cordio)

Add BDD assumptions

Add Behavior Driven Development style entry point for assumption methods for different types, which allow to skip test execution when assumptions are not met.

The difference with the Assumptions class is that entry point methods are named given instead of assumeThat.

Example:

String hobbit = "HOBBIT";
List<String> fellowshipOfTheRing = list("Aragorn", "Gandalf", "Frodo", "Legolas");

@Test
public void given_the_assumption_is_not_met_the_test_is_skipped() {
  given(hobbit).isEqualTo("ORC");
  // ... following code is not executed, the test is skipped
  then(fellowshipOfTheRing).contains("Sauron");
}

@Test
public void given_the_assumption_is_met_the_test_is_executed() {
  given(hobbit).isEqualTo("HOBBIT");
  // ... following code is executed and fails!
  then(fellowshipOfTheRing).doesNotContain("Sauron");
}

Add Spliterator assertions

Add hasCharacteristics and hasOnlyCharacteristics assertions for the Spliterator type.

Example:

Spliterator<Integer> spliterator = Stream.of(1, 2, 3).spliterator();

assertThat(spliterator).hasCharacteristics(Spliterator.SIZED,
                                           Spliterator.ORDERED)
                       .hasOnlyCharacteristics(Spliterator.SIZED,
                                               Spliterator.SUBSIZED,
                                               Spliterator.IMMUTABLE,
                                               Spliterator.ORDERED);

Add isAtSameInstantAs to OffsetDateTime assertions

Verifies that actual and given OffsetDateTime are at the same Instant.

Example:

OffsetDateTime offsetDateTime1 = OffsetDateTime.of(2000, 12, 12, 3, 0, 0, 0, ZoneOffset.ofHours(3));
OffsetDateTime offsetDateTime2 = OffsetDateTime.of(2000, 12, 12, 0, 0, 0, 0, ZoneOffset.ofHours(0));
// assertion succeeds
assertThat(offsetDateTime1).isAtSameInstantAs(offsetDateTime2);

offsetDateTime2 = OffsetDateTime.of(2000, 12, 12, 2, 0, 0, 0, ZoneOffset.ofHours(0));
// assertion fails
assertThat(offsetDateTime1).isAtSameInstantAs(offsetDateTime2);

Add assertAlso SoftAssertions method to allow combining different soft assertions instances

assertAlso lets you combine other soft assertions instances together.

Example:

Mansion mansion = new Mansion();

SoftAssertions check_kitchen() {
  SoftAssertions softly = new SoftAssertions();
  softly.assertThat(mansion.kitchen()).as("Kitchen").isEqualTo("clean");
  return softly;
}

SoftAssertions check_library() {
  SoftAssertions softly = new SoftAssertions();
  softly.assertThat(mansion.library()).as("Library").isEqualTo("clean");
  return softly;
}

@Test
void host_dinner_party_where_nobody_dies() {
  SoftAssertions softly = new SoftAssertions();
  mansion.hostPotentiallyMurderousDinnerParty();
  softly.assertThat(mansion.guests()).as("Living Guests").isEqualTo(7);
  softly.assertThat(mansion.revolverAmmo()).as("Revolver Ammo").isEqualTo(6);
  softly.assertThat(mansion.candlestick()).as("Candlestick").isEqualTo("pristine");
  softly.assertThat(mansion.colonel()).as("Colonel").isEqualTo("well kempt");
  softly.assertThat(mansion.professor()).as("Professor").isEqualTo("well kempt");

  SoftAssertions kitchen = check_kitchen();
  softly.assertAlso(kitchen);

  SoftAssertions library = check_library();
  softly.assertAlso(library);

  softly.assertAll();
}

Add isEmpty and isNotEmpty file assertions

Verify that the actual File is empty (i.e. the file size = 0) or not empty (i.e. the file size > 0) .

Example:

File file = File.createTempFile("tmp", "txt");

// assertion will pass
assertThat(file).isEmpty();

Files.write(file.toPath(), new byte[]{1, 1});

// assertion will pass
assertThat(file).isNotEmpty();

Add hasSize(long expectedSizeInBytes) to File assertions

Verifies that the size of the File under test is exactly equal to the given size in bytes.

Example:

File file = File.createTempFile("tmp", "bin");
Files.write(file.toPath(), new byte[] {1, 1});

// assertion will pass
assertThat(file).hasSize(2);

// assertion will fail
assertThat(file).hasSize(1);

Avoid BDDMockito/BDDAssertions then(object) clash with and.then(object)

To avoid clash with libraries like Mockito that exposes a static then(object) method, you can statically use the and field.

import static org.mockito.BDDMockito.then;
// can't use import static org.assertj.core.api.BDDAssertions.then because of BDDMockito.then;
import static org.assertj.core.api.BDDAssertions.and;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;

// suppress and.then warning: The static method BDDAssertions.then() should be accessed in a static way
@SuppressWarnings("static-access")
@Test
public void bdd_assertions_with_bdd_mockito() {
  // GIVEN
  Person person = mock(Person.class)
  // WHEN
  person.ride(bike);
  person.ride(bike);
  // THEN
  // mockito then()
  then(person).should(times(2)).ride(bike);
  // use AssertJ and.then(person) as then(person) would clash with mockito then(person)
  and.then(person.hasBike()).isTrue();
}

Add hasRootCauseMessage to Throwable assertions

Verifies that the message of the root cause of the actual Throwable is equal to the given one, a simple String or String.format is supported to specify the expected root cause message.

Example:

Throwable throwable = new Throwable(new IllegalStateException(new NullPointerException("expected message")));

// assertions will pass
assertThat(throwable).hasRootCauseMessage("expected message")
                     .hasRootCauseMessage("expected %s", "message");

// assertions will fail
assertThat(throwable).hasRootCauseMessage("another message");
assertThat(throwable).hasRootCauseMessage("%s", "message");
// no root cause message
assertThat(new Throwable()).hasRootCauseMessage("%s %s", "expected", "message");

Add syntax sugar as(InstanceOfAssertFactory) to Assertions and WithAssertions for improved readability

A syntax sugar to write fluent assertion with methods having an InstanceOfAssertFactory parameter. Added as a static method in Assertions, it is also available as a default method in the WithAssertions interface.

Example:

Jedi yoda = new Jedi("Yoda", "Green");

assertThat(yoda).extracting(Jedi::getName, as(InstanceOfAssertFactories.STRING))
                .startsWith("Yo");

as(InstanceOfAssertFactory) can be used together with the following assertion methods:

Add extracting with String and InstanceOfAssertFactory parameters to Object assertions

Extracts the value of given field/property from the object under test, the extracted value becoming the new object under test. The InstanceOfAssertFactory parameter is used to get the assertions narrowed to the factory type.

Examples:

// Create frodo, setting its name, age and Race (Race having a name property)
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);

// let's extract and verify Frodo's name:
assertThat(frodo).extracting("name", as(InstanceOfAssertFactories.STRING))
                 .startsWith("Fro");

// The following assertion will fail as Frodo's name is not an Integer:
assertThat(frodo).extracting("name", as(InstanceOfAssertFactories.INTEGER))
                 .isZero();

Add extracting with Function and InstanceOfAssertFactory parameters to Object assertions

Uses the given Function to extract a value from the object under test, the extracted value becoming the new object under test. The InstanceOfAssertFactory parameter is used to get the assertions narrowed to the factory type.

Examples:

// Create frodo, setting its name, age and Race (Race having a name property)
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);

// let's extract and verify Frodo's name:
assertThat(frodo).extracting(TolkienCharacter::getName, as(InstanceOfAssertFactories.STRING))
                 .startsWith("Fro");

// The following assertion will fail as Frodo's name is not an Integer:
assertThat(frodo).extracting(TolkienCharacter::getName, as(InstanceOfAssertFactories.INTEGER))
                 .isZero();

Add extractingByKey with KEY and InstanceOfAssertFactory parameters to Map assertions

Extracts the value of given key from the map under test, the extracted value becoming the new object under test. The InstanceOfAssertFactory parameter is used to get the assertions narrowed to the factory type.

Examples:

Map<String, Object> map = new HashMap<>();
map.put("name", "kawhi");

// The following assertion will succeed:
assertThat(map).extractingByKey("name", as(InstanceOfAssertFactories.STRING))
               .startsWith("kaw");

// The following assertion will fail as the value is not an Integer:
assertThat(map).extractingByKey("name", as(InstanceOfAssertFactories.INTEGER))
               .isZero();

Add get with InstanceOfAssertFactory parameters to Optional assertions

Verifies that the optional is not null and not empty and returns an new assertion instance to chain assertions on the optional value. The InstanceOfAssertFactory parameter is used to get the assertions narrowed to the factory type.

Examples:

Optional<String> optional = Optional.of("Frodo");

// The following assertion will succeed:
assertThat(optional).get(as(InstanceOfAssertFactories.STRING))
                    .startsWith("Fro");

// The following assertion will fail as the value is not an Integer:
assertThat(optional).get(as(InstanceOfAssertFactories.INTEGER))
                    .isZero();

Add first with InstanceOfAssertFactory parameters to Iterable assertions

Navigates and allows to perform assertions on the first element of the Iterable under test. The InstanceOfAssertFactory parameter is used to get the assertions narrowed to the factory type.

Examples:

Iterable<String> hobbits = newArrayList("Frodo", "Sam", "Pippin");

// assertion succeeds
assertThat(hobbits).first(as(InstanceOfAssertFactories.STRING))
                   .startsWith("Fro")
                   .endsWith("do");
// assertion fails
assertThat(hobbits).first(as(InstanceOfAssertFactories.STRING))
                   .startsWith("Pip");
// assertion fails because of wrong factory type
assertThat(hobbits).first(as(InstanceOfAssertFactories.INTEGER))
                   .isZero();

Add last with InstanceOfAssertFactory parameters to Iterable assertions

Navigates and allows to perform assertions on the last element of the Iterable under test. The InstanceOfAssertFactory parameter is used to get the assertions narrowed to the factory type.

Examples:

Iterable<String> hobbits = newArrayList("Frodo", "Sam", "Pippin");

// assertion succeeds
assertThat(hobbits).last(as(InstanceOfAssertFactories.STRING))
                   .startsWith("Pip")
                   .endsWith("pin");
// assertion fails
assertThat(hobbits).last(as(InstanceOfAssertFactories.STRING))
                   .startsWith("Fro");
// assertion fails because of wrong factory type
assertThat(hobbits).last(as(InstanceOfAssertFactories.INTEGER))
                   .isZero();

Add element with InstanceOfAssertFactory parameters to Iterable assertions

Navigates and allows to perform assertions on the chosen element of the Iterable under test. The InstanceOfAssertFactory parameter is used to get the assertions narrowed to the factory type.

Examples:

Iterable<String> hobbits = newArrayList("Frodo", "Sam", "Pippin");

// assertion succeeds
assertThat(hobbits).element(1, as(InstanceOfAssertFactories.STRING))
                   .startsWith("Sa")
                   .endsWith("am");
// assertion fails
assertThat(hobbits).element(1, as(InstanceOfAssertFactories.STRING))
                   .startsWith("Fro");
// assertion fails because of wrong factory type
assertThat(hobbits).element(1, as(InstanceOfAssertFactories.INTEGER))
                   .isZero();

Add String.format support for expected message in hasMessageStartingWith, hasMessageContaining, hasMessageEndingWith and hasStackTraceContaining assertions

Instead of taking a simple String the assertions mentioned above now accept a String.format like parameters, i.e. (String description, Object…​ parameters) making it easier to build more involved expected strings.

Examples:

Throwable throwableWithMessage = new IllegalArgumentException("wrong amount 123");

assertThat(throwableWithMessage).hasMessageStartingWith("%s a", "wrong")
                                .hasMessageContaining("wrong %s", "amount")
                                .hasMessageEndingWith("%s 123", "amount")
                                .hasStackTraceContaining("%s amount", "wrong");

ObjectAssert.extracting(String…​) learned to extract nested map key field/property

extracting is now able to extract a deeply nested map key, before this improvement extracting a value by key was only supported for a Map object under test (but not for fields of type Map).

Let’s clarify things with a concrete example:

Jedi luke = new Jedi(new Name("Luke", "Skywalker"), 26);
// setAttribute puts a new entry in 'attributes' Map field
luke.setAttribute("side", "light");

Jedi leia = new Jedi(new Name("Leia", "Skywalker"), 26);
// setRelation puts a new entry in 'relations' Map field
luke.setRelation("sister", leia);
leia.setRelation("brother", luke);

assertThat(luke).extracting("name.last",
                            "attributes.side",
                            "relations.sister",
                            "relations.sister.relations.brother")
                .containsExactly("Skywalker",
                                 "light",
                                 leia,
                                 luke);

Prettify allOf and anyOf combined conditions description

To make it more readable, reformat the error message when multiple combined conditions with allOf and anyOf fail.

Examples: the following assertion will fail …​

private static Condition<String> contains(String s) {
  return new Condition<>(value -> value.contains(s), "contains " + s);
}

// failing assertion:
assertThat("Gandalf").has(anyOf(contains("i"),
                                allOf(contains("o"),
                                      anyOf(contains("a"),
                                            contains("b"),
                                            contains("c")))));

With the following error message

Expecting:
 <"Gandalf">
to have:
 <any of:[
   contains i,
   all of:[
      contains o,
      any of:[
         contains a,
         contains b,
         contains c
      ]
   ]
]>

Add matching syntactic sugar method to use Hamcrest Matcher as Condition

Syntactic sugar to construct a Condition using the Hamcrest Matcher given as a parameter.

Example:

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.HamcrestCondition.matching;
import static org.hamcrest.core.StringContains.containsString;

@Test
public void matching_example() {
 assertThat("abc").is(matching(containsString("a")));
}

AssertJ Core 3.13.2

Release date: 2019-08-04

This release ships a few improvements:

  • Fixes an annoyance in InstanceOfAssertFactories, where URL and URI constants have been renamed to URL_TYPE and URI_TYPE respectively to avoid a clash with java.net.URL and java.net.URI. See github.com/assertj/assertj-core/issues/1567 for details.

  • Updates ByteBuddy to version 1.10.0.

  • Fixes some Javadoc typos.

  • Enforces banned dependencies with maven-enforcer-plugin.

AssertJ Core 3.13.1

Release date: 2019-07-29

This release addresses the 3.13.0 issue by which AssertJ required OpenTest4J to be on the classpath otherwise a java.lang.NoClassDefFoundError: org/opentest4j/MultipleFailuresError would be raised. Thanks Pascal Schumacher for the quick fix!

java.lang.NoClassDefFoundError: org/opentest4j/MultipleFailuresError
   at java.base/java.lang.ClassLoader.defineClass1(Native Method)
   at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
   at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
   at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:802)
   at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:700)
   at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:623)
   at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
   at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
   at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
   at org.assertj.core.internal.Failures.<init>(Failures.java:46)
   at org.assertj.core.internal.Failures.<clinit>(Failures.java:44)
   at org.assertj.core.internal.Objects.<init>(Objects.java:87)
   at org.assertj.core.internal.Objects.<init>(Objects.java:101)
   at org.assertj.core.internal.Objects.<clinit>(Objects.java:82)
   at org.assertj.core.api.AbstractAssert.<init>(AbstractAssert.java:65)
   at org.assertj.core.api.AbstractCharSequenceAssert.<init>(AbstractCharSequenceAssert.java:53)
   at org.assertj.core.api.AbstractStringAssert.<init>(AbstractStringAssert.java:28)
   at org.assertj.core.api.StringAssert.<init>(StringAssert.java:25)
   at org.assertj.core.api.AssertionsForClassTypes.assertThat(AssertionsForClassTypes.java:484)
   at org.assertj.core.api.Assertions.assertThat(Assertions.java:2585)

AssertJ Core 3.13.0

Release date: 2019-07-28

The highlight of this release is the addition of asInstanceOf which allows to chain specific type assertions from a value that was initially declared with a different type (usually Object). Thanks Stefano Cordio for this contribution!

Example:

Object value = "abc";

// This line DOES NOT COMPILE since startsWith is a String assertion and value is an Object
assertThat(value).startsWith("ab");

// This line COMPILES because we tell AssertJ to consider value as a String
assertThat(value).asInstanceOf(InstanceOfAssertFactories.STRING).startsWith("ab");

This feature is more detailed in the notes below.

Contributors

Thanks to all the contributors of this release:

Pascal Schumacher, Erhard Pointl, Stefano Cordio, Thomas Traude, Andrei Solntsev, Matej Drobnič, Željko Mirović, Mike Gilchrist, Phillip Webb, Michal Fotyga,Valeriy Vyrva, Eddú Meléndez Gonzales, GaspardPO, Bengt Brodersen, Jiri Pejchal, Christian Stein, Nikolaos Georgiou and Sam Brannen.

Special thanks to Nils Winkler for his work on the assertions conversion scripts and Stefano Cordio for the asInstanceOf contribution.

Breaking changes

  • As the extracting(String) method for Object and Map extracts only one value, it now returns Object assertions instead of list assertions (on a singleton list). This means that any list assertions used won’t compile anymore, they need to be replaced by Object assertions.

// GIVEN
Map<String, Object> basketballPlayer = new HashMap<>();
basketballPlayer.put("name", "kawhi");
basketballPlayer.put("age", 25);

// Does not compile anymore!
assertThat(basketballPlayer).extracting("name")
                            .containsExactly("kawhi"); // DOES NOT COMPILE

// Use Object assertions like isEqualTo
assertThat(basketballPlayer).extracting("name")
                            .isEqualTo("kawhi");

// multiple values work as before, no problem there!
assertThat(basketballPlayer).extracting("name", "age")
                            .containsExactly("kawhi", 25);
  • In the new recursive comparison, we now use the expected field as a reference to determine how to compare it to corresponding the actual field. Sorted vs non sorted collections comparison semantics have been replaced by ordered vs unordered collections semantics (ordered types are List, SortedSet and LinkedHashSet). As a consequence of the two previous points, when comparing collection/map fields, if the actual field is ordered and the expected is unordered, the comparison is allowed but not the other way around (unless order is ignored explicitely in the comparison configuration).

New features

Improvements

  • Junit 4/5 and TestNG assertions convertion scripts improvements. (Nils Winkler)

  • Add support for combined millisecond and timezone parsing. (Matej Drobnič)

  • Add support for Optional in the new recursive comparison.

  • Allow ignoring collection order in specific fields in the new recursive comparison. (Željko Mirović)

  • Make catchThrowableOfType easier to discover in the Javadoc.

  • Rename methods isBeforeOrEqualsTo and isAfterOrEqualsTo to isBeforeOrEqualTo and isAfterOrEqualTo. (Eddú Meléndez Gonzales)

  • Improve error messages in the new recursive comparison when group size differs or when trying to compare actual unordered vs expected ordered.

  • Introduce explicit module descriptor. (Christian Stein)

  • Allow returned values of WithAssertions#fail methods to be ignored by FindBugs/SpotBugs. (Jiri Pejchal)

  • Improve the error message when multiple (soft) assertions error are raised.

  • Propagate value type with extracting(Function). (Stefano Cordio)

Fixed

  • Fix Soft assertions JUnit 5 extension that did not support parallel test nor @TestInstance(PER_CLASS) lifecycle semantics. (Sam Brannen)

  • Fix JavaDoc regarding AnyOf and AllOf. (Thomas Traude)

  • Make sure that isEqualTo("abc") is not resolved to isEqualTo(String, Object…​ args). (Andrei Solntsev)

  • Fix Javadoc typos. (GaspardPO, Michal Fotyga)

  • Fix typo in error message factories ShouldBeBeforeOrEqualTo and ShouldBeAfterOrEqualTo. (Stefano Cordio)

Deprecated

  • Deprecate Java 6/Android assertions entry points as they don’t truly provide 100% Java 6/Android compatibility.

  • Deprecate methods isBeforeOrEqualTo and isAfterOrEqualTo in favor of isBeforeOrEqualsTo and isAfterOrEqualsTo (Eddú Meléndez Gonzales).

  • Deprecate JUnitJupiterSoftAssertions and JUnitJupiterBDDSoftAssertions in favor of SoftAssertionsExtension

Add asInstanceOf to chain specific type assertions

asInstanceOf allows to chain specific type assertions from a value initially declared as a less specific type (often Object).

Let’s start with the problem asInstanceOf is solving: in the following example we would like to call String assertions but this is not possible since value is declared as an Object thus only Object assertions are accessible.

// Given a String declared as an Object
Object value = "Once upon a time in the west";

// We would like to call String assertions but this is not possible since value is declared as an Object
assertThat(value).startsWith("ab"); // this does not compile !

Thanks to asInstanceOf we can now tell AssertJ to consider value as a String in order to call String assertions. To do so we need to pass an InstanceOfAssertFactory that can build a StringAssert, fortunately you don’t have to write it, it is already available in InstanceOfAssertFactories!

import static org.assertj.core.api.InstanceOfAssertFactories.STRING;

// Given a String declared as an Object
Object value = "Once upon a time in the west";

// With asInstanceOf, we switch to specific String assertion by specifying the InstanceOfAssertFactory for String
assertThat(value).asInstanceOf(STRING).startsWith("Once");

AssertJ verifies that the actual value is compatible with the assertions InstanceOfAssertFactory is going to give access to.

InstanceOfAssertFactories provides static factories for all types AssertJ provides assertions for, additional factories can be created with custom InstanceOfAssertFactory instances.

Here’s another example showing the parameterized type support:

// Actually a List<TolkienCharacter>
Object hobbits = list(frodo, pippin, merry, sam);

// As we specify the TolkienCharacter class, the following chained assertion expect to be given TolkienCharacters.
// This means that method like extracting or filteredOn are given a TolkienCharacter
assertThat(hobbits).asInstanceOf(InstanceOfAssertFactories.list(TolkienCharacter.class))
                   .contains(frodo, sam)
                   .extracting(TolkienCharacter::getName)
                   .contains("Frodo", "Sam");

// Use LIST if the elements type is not important but note that the chained assertions
// will be given Object not TolkienCharacter
assertThat(hobbits).asInstanceOf(InstanceOfAssertFactories.LIST)
                    //.extracting(TolkienCharacter::getName) does not work as extracting is given an Object
                   .contains(frodo);

Add extracting with single parameter to Object and Map assertions

Extracts the value of given field/property from the object under test, the extracted value becoming the new object under test.

Examples:

// Create frodo, setting its name, age and Race (Race having a name property)
TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, HOBBIT);

// let's extract and verify Frodo's name:
assertThat(frodo).extracting("name")
                 .isEqualTo("Frodo");

// The extracted value being a String, we would like to use String assertions but we can't due to Java generics limitations.
// The following assertion does NOT compile:
assertThat(frodo).extracting("name")
                 .startsWith("Fro");

// To get String assertions use asInstanceOf:
assertThat(frodo).extracting("name")
                 .asInstanceOf(InstanceOfAssertFactories.STRING)
                 .startsWith("Fro");

If the object under test is a Map, the parameter is used as a key to the map.

Example:

Map<String, Object> basketballPlayer = new HashMap<>();
basketballPlayer.put("name", "kawhi");
basketballPlayer.put("age", 25);

// single value
assertThat(basketballPlayer).extracting("name")
                            .isEqualTo("kawhi");

AssertJ global configuration

AssertJ 3.13.0 introduces a Configuration class allowing to change AssertJ behavior and a way to register automatically. Read Configuring AssertJ chapter to learn about it.

Add hasCauseReference to throwable assertions

Verifies that the actual Throwable has a cause that refers to the given one, i.e. using == comparison.

Example:

Throwable invalidArgException = new IllegalArgumentException("invalid arg");
Throwable throwable = new Throwable(invalidArgException);

// This assertion succeeds:
assertThat(throwable).hasCauseReference(invalidArgException);

// These assertions fail:
assertThat(throwable).hasCauseReference(new IllegalArgumentException("invalid arg"));
assertThat(throwable).hasCauseReference(new NullPointerException());
assertThat(throwable).hasCauseReference(null); // prefer hasNoCause()

New directory content assertions

The new assertions have been added for both File and Path, they add support for

Both isDirectoryContaining and isDirectoryNotContaining accept either Predicate or String parameters, the String one being interpreted as a path matcher.

As File and Path assertions are similar, the examples will only show File assertions.

The examples use the following directory structure:

/root/
/root/sub-dir-1/
/root/sub-dir-1/file-1.ext
/root/sub-dir-1/file-2.ext
/root/sub-dir-2/
/root/sub-file-1.ext
/root/sub-file-2.ext

isDirectoryContaining assertions examples:

File root = new File("root");

// Successfull assertions with predicate parameter:
assertThat(root).isDirectoryContaining(file -> file.getName().startsWith("sub-dir"))
                .isDirectoryContaining(file -> file.getName().startsWith("sub-file"))
                .isDirectoryContaining(file -> file.getName().endsWith(".ext"))
                .isDirectoryContaining(File::isDirectory);

// Successfull assertions with String path matcher parameter:
assertThat(root).isDirectoryContaining("glob:**sub-dir*")
                .isDirectoryContaining("glob:**sub-file*")
                .isDirectoryContaining("glob:**.ext")
                .isDirectoryContaining("regex:.*ext")
                .isDirectoryContaining("glob:**.{ext,bin");


// The following assertions fail:
assertThat(root).isDirectoryContaining(file -> file.getName().startsWith("dir"));
assertThat(root).isDirectoryContaining(file -> file.getName().endsWith(".bin"));
assertThat(root).isDirectoryContaining("glob:**dir");
assertThat(root).isDirectoryContaining("glob:**.bin");

isDirectoryNotContaining assertion examples:

File root = new File("root");

// Successfull assertions with predicate parameter:
assertThat(root).isDirectoryNotContaining(file -> file.getName().startsWith("dir"))
                .isDirectoryNotContaining(file -> file.getName().endsWith(".bin"));

// Successfull assertions with String path matcher parameter:
assertThat(root).isDirectoryNotContaining("glob:**dir")
                .isDirectoryNotContaining("glob:**.bin")
                .isDirectoryNotContaining("regex:.*bin")
                .isDirectoryNotContaining("glob:**.{java,class}");

// The following assertions fail:
assertThat(root).isDirectoryContaining(file -> file.getName().startsWith("dir"));
assertThat(root).isDirectoryContaining(file -> file.getName().endsWith(".bin"));
assertThat(root).isDirectoryNotContaining("glob:**sub-dir*");
assertThat(root).isDirectoryNotContaining("regex:.*ext");
assertThat(root).isDirectoryNotContaining("glob:**.{ext,bin");

isEmptyDirectory assertion examples:

File root = new File("root");

// The following assertion succeeds:
assertThat(new File(root, "sub-dir-2")).isEmptyDirectory();

// The following assertions fail:
assertThat(root).isEmptyDirectory();
assertThat(new File(root, "sub-dir-1")).isEmptyDirectory();

isNotEmptyDirectory assertion examples:

File root = new File("root");

// The following assertions succeed:
assertThat(root).isNotEmptyDirectory();
assertThat(new File(root, "sub-dir-1")).isNotEmptyDirectory();

// The following assertion fails:
 assertThat(new File(root, "sub-dir-2")).isNotEmptyDirectory();

Add hasMessageContainingAll and hasMessageNotContainingAny to throwable assertions

These assertions are the equivalent of hasMessageContaining and hasMessageNotContaining but accepting multiple String parameters instead of only one.

Example:

Throwable throwableWithMessage = new IllegalArgumentException("wrong amount 123");
Throwable throwableWithoutMessage = new IllegalArgumentException();

// assertion will pass:
assertThat(throwableWithMessage).hasMessageContainingAll("amount", "123")
                                .hasMessageNotContainingAny("foo", "234");

assertThat(throwableWithoutMessage).hasMessageNotContainingAny("234");

// assertions will fail:
assertThat(throwableWithMessage).hasMessageContainingAll("234");
assertThat(throwableWithoutMessage).hasMessageContainingAll("123");

assertThat(throwableWithMessage).hasMessageNotContainingAny("foo", "amount");

The same assertions have been added to ThrowableAssertAlternative with these names withMessageContainingAll and withMessageNotContainingAny:

Throwable illegalArgumentException = new IllegalArgumentException("wrong amount 123");

// assertions will pass
assertThatExceptionOfType(Throwable.class)
          .isThrownBy(() -> {throw illegalArgumentException;})
          .withMessageContainingAll("amount", "123")
          .withMessageNotContainingAny("foo", "234");

Allow using any custom assertions in soft assertions

The new check method catches and collect assertion errors coming from standard and custom assertions.

Example:

SoftAssertions softly = new SoftAssertions();

// custom assertions
softly.check(() -> LotrAssertions.assertThat(frodo).hasName("Frodon"));
softly.check(() -> LotrAssertions.assertThat(frodo).hasName("Frodo"));

// standard assertions
softly.assertThat("foo").startsWith("bar");
// could be written with check like (but it's as elegant as the standard use):
// softly.check(() -> Assertions.assertThat("foo").startsWith("bar"));

// 2 errors: "foo" does not start with "bar" and frodo's name is not "Frodon"
assertThat(softly.errorsCollected()).hasSize(2);

Add containsExactlyInAnyOrderEntriesOf to map assertions

Verifies that the actual map contains only the given entries and nothing else, in any order.

This is the same assertion as containsOnly(Map.Entry…​ entries), it simply handles the conversion of Map.entrySet() to array.

Example :

Map<Ring, TolkienCharacter> ringBearers = newLinkedHashMap(entry(oneRing, frodo),
                                                           entry(nenya, galadriel),
                                                           entry(narya, gandalf));
// assertion will pass
assertThat(ringBearers).containsExactlyInAnyOrderEntriesOf(newLinkedHashMap(entry(oneRing, frodo),
                                                                            entry(nenya, galadriel),
                                                                            entry(narya, gandalf)));
// assertion will pass although actual and expected order differ
assertThat(ringBearers).containsExactlyInAnyOrderEntriesOf(newLinkedHashMap(entry(nenya, galadriel),
                                                                            entry(narya, gandalf),
                                                                            entry(oneRing, frodo)));
// assertion will fail as actual does not contain all entries of expected
assertThat(ringBearers).containsExactlyInAnyOrderEntriesOf(newLinkedHashMap(entry(oneRing, frodo),
                                                                            entry(nenya, galadriel),
                                                                            entry(oneRing, frodo)));
// assertion will fail as actual and expected have different sizes
assertThat(ringBearers).containsExactlyInAnyOrderEntriesOf(newLinkedHashMap(entry(oneRing, frodo),
                                                                            entry(nenya, galadriel),
                                                                            entry(narya, gandalf),
                                                                            entry(narya, gandalf)));

Add isCloseToUtcNow to LocalDateTime and OffsetDateTime assertions

Verifies that the actual LocalDateTime/OffsetDateTime is close to the current date and time on the UTC timezone, according to the given offset.

You can build the offset parameter using Assertions.within(long, TemporalUnit) or Assertions.byLessThan(long, TemporalUnit).

If the difference is equal to the offset, the assertion succeeds.

Example with LocalDateTime:

LocalDateTime actual = LocalDateTime.now(Clock.systemUTC());

// assertion will pass if executed less than one second after actual was built
assertThat(actual).isCloseToUtcNow(byLessThan(1, ChronoUnit.SECONDS));

// assertion will fail
assertThat(actual.plusSeconds(2)).isCloseToUtcNow(within(1, ChronoUnit.SECONDS));

The same example works with OffsetDateTime by simply defining actual as:

OffsetDateTime actual = OffsetDateTime.now(Clock.systemUTC());

Add support for combined millisecond and timezone parsing

Add yyyy-MM-dd HH:mm:ss.SSSX to the default date formats AssertJ supports in Date assertions that take a String parameter representating a Date.

Here’s an example of string following this format: "2003-04-26T00:00:00.123+00:00".

Example:

// GIVEN
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
// WHEN
Date date = isoFormat.parse("2003-04-26T00:00:00.123");
// THEN
assertThat(date).isEqualTo("2003-04-26T00:00:00.123+00:00");

Add support for Optional in the new recursive comparison

The recursive comparison added in 3.12.0 now compares Optional values recursively instead of comparing Optional with equals. This is consistent with comparing list elements by elements as an Optional can be seen as a list with at most one element.

Example:

// Song constructor parameters: song, author and coAuthor (optional)
Song song = new Song("I Can't Get No Satisfaction", new Author("Mick Jagger"), new Author("Keith Richards"));
Song expectedSong = new Song("I Can't Get No Satisfaction", new Author("Mick Jagger"), new Author("Keith Richards"));
// THEN
assertThat(song).usingRecursiveComparison()
                .isEqualTo(expectedSong);

where Song and Author don’t override equals:

class Song {

  Author author;
  Optional<Author> coAuthor;
  String song;

  Song(String song, Author author, Author coAuthor) {
    this.song = song;
    this.author = author;
    this.coAuthor = Optional.ofNullable(coAuthor);
  }

  // no equals!
}

class Author {

  String name;

  Author(String name) {
    this.name = name;
  }

  String getName() {
    return name;
  }

  // no equals!
}

If we fail the test ...

Song song = new Song("I Can't Get No Satisfaction", new Author("Mick Jagger"), new Author("Jimi Hendrix"));
Song expectedSong = new Song("I Can't Get No Satisfaction", new Author("Mick Jagger"), new Author("Keith Richards"));
// FAIL
assertThat(song).usingRecursiveComparison()
                .isEqualTo(expectedSong);

... here’s the error reported:

Expecting:
  <Song [author=Mick Jagger, coAuthor=Optional[Jimi Hendrix], song=I Can't Get No Satisfaction]>
to be equal to:
  <Song [author=Mick Jagger, coAuthor=Optional[Keith Richards], song=I Can't Get No Satisfaction]>
when recursively comparing field by field, but found the following difference:

field/property 'coAuthor.value.name' differ:
- actual value   : "Jimi Hendrix"
- expected value : "Keith Richards"

The recursive comparison was performed with this configuration:
- overridden equals methods were used in the comparison
- these types were compared with the following comparators:
  - java.lang.Double -> DoubleComparator[precision=1.0E-15]
  - java.lang.Float -> FloatComparator[precision=1.0E-6]
- actual and expected objects and their fields were compared field by field recursively even if they were not of the same type, this allows for example to compare a Person to a PersonDto (call strictTypeChecking(true) to change that behavior).

Allow ignoring collection order in the new recursive comparison

The recursive comparison added in 3.12.0 can now ignore collection order in all fields in the object under test, this is handy when comparing list to set fields where only the content is relevant but not the order.

Example:

public class Person {
  String name;
  List<Person> friends = new ArrayList<>();
}

Person sherlock1 = new Person("Sherlock Holmes");
sherlock1.friends.add(new Person("Dr. John Watson"));
sherlock1.friends.add(new Person("Molly Hooper"));

Person sherlock2 = new Person("Sherlock Holmes");
sherlock2.friends.add(new Person("Molly Hooper"));
sherlock2.friends.add(new Person("Dr. John Watson"));

// assertion succeeds as all fields collection order is ignored in the comparison
assertThat(sherlock1).usingRecursiveComparison()
                     .ignoringCollectionOrder()
                     .isEqualTo(sherlock2);

// assertion fails as fields collection order is not ignored in the comparison
assertThat(sherlock1).usingRecursiveComparison()
                     .isEqualTo(sherlock2);

Propagate value type with extracting(Function)

extracting(Function) learned to propagate the type parameter of the resulting ObjectAssert allowing then to chain other type aware methods (e.g. additional extracting).

Example:

// Old implementation
assertThat(yoda).extracting(Jedi::getName) // ObjectAssert<Object>
                .extracting(String::length) // Not compiling
                .isEqualTo(4);

// New implementation
assertThat(yoda).extracting(Jedi::getName) // ObjectAssert<String>
                .extracting(String::length)  // Compiling!
                .isEqualTo(4);

AssertJ Core 3.12.2

The main issue fixed was to ignore static methods when finding property accessors (contributed by Andy Wilkinson) which could break some tests since bare name method introspection was introduced in 3.12.0.

anySatisfy for Maps was improved and does not continue evaluating elements once a match is found (contributed by Erhard Pointl).

AssertJ Core 3.12.1

Fix a regression that included a bad module-info.class (thanks Jaro Kuruc) and other minor improvements.

AssertJ Core 3.12.0

Release date: 2019-02-14

The main feature of this release is a beta version of the new Recursive comparison API! It covers what isEqualToComparingFieldByFieldRecursively used but easier to use and with more capabilities.

It is a Beta version because we want to have feedback from the community to make it even better before freezing the API. There are more capabilities to come in the next releases, stay tuned!

Contributors

Big thanks to all the contributors of this release:

Pascal Schumacher, Erhard Pointl, Vladimir Chernikov, Sandra Parsick, Martin Tarjanyi, Stephan Windmüller, Yaroslav Mitsynskiy, Thomas Traude, Georg Berky, Tomek Kaczanowski, Lukáš Křečan, Yoann Rodière, Filip Hrisafov, Steven Schlansker, Jeremy Landis, Jack Gough, Sebastian Kempken, Stefan Mandel, Alexandre de Champeaux, Arvid Heise, Jeff Walker, Dmitrii Priporov and Joshua Kitchen.

Breaking changes

  • Introduce first class Iterator assertions (Stephan Windmüller).

This removes the previously supported “Iterable” assertions (like containsOnly), call IteratorAssert#toIterable to access them again, ex:
Iterator<String> bestBasketBallPlayers = getBestBasketBallPlayers();

assertThat(bestBasketBallPlayers).toIterable().contains("Jordan", "Magic", "Lebron");
  • Add configurable support for bare-named property introspection. (Steven Schlansker)

AssertJ uses introspection in various places, one example is extracting properties as in extracting("name"). AssertJ is able to get values with getters like getName(), with this improvement it now can get property values with bare name method like name().

Bare-named property introspection is enabled by default and thus changes AssertJ behavior which can break some existing tests relying on introspection, this is especially true as AssertJ wrongly tries static methods (github.com/assertj/assertj-core/issues/1458 had been created to address that).

It is possible to avoid this problem by calling Assertions.setExtractBareNamePropertyMethods(false); before every impacted tests.

This is a bit tedious but an improvement is planned in the next release to provide a place to perform global configuration with the same mechanism allowing to register a custom representation.

New features

  • New Recursive comparison API! (Beta version)

  • Add satisfiesAnyOf base assertion.

  • Add isAbstract to Class assertions. (Erhard Pointl)

  • Add hasValueCloseTo(percentage) to OptionalDouble assertion. (Joshua Kitchen)

  • Add hasOnlyOneElementSatisfying(Consumer) to AtomicReferenceArray assertions. (Vladimir Chernikov)

  • Add hasAllNullFieldsOrProperties and hasAllNullFieldsOrPropertiesExcept. (Vladimir Chernikov)

  • Add hasSizeGreaterThan, hasSizeLessThanOrEqualTo, hasSizeGreaterThanOrEqualTo and hasSizeGreaterThan to CharSequence and String assertions. (Sandra Parsick)

  • Add hasSizeGreaterThan, hasSizeLessThanOrEqualTo, hasSizeGreaterThanOrEqualTo, hasSizeGreaterThan and hasSizeBetween to object and primitives array, Iterable and Map. (Martin Tarjanyi)

  • Add hasSizeBetween to CharSequence and String assertions. (Martin Tarjanyi)

  • Add noneSatisfy(BiConsumer) to Map assertions. (Erhard Pointl)

  • Add containsExactlyEntriesOf assertion to check that a Map contains exactly all entries of another Map. (Filip Hrisafov)

  • Add containsOnlyKeys(Iterable keys) to Map assertion. (Sebastian Kempken)

  • Add anySatifies(BiConsumer) to Map assertion. (Stefan Mandel)

  • Add hasMessageNotContaining to Throwable assertions. (Georg Berky and Sandra Parsick)

  • Add shouldHaveRootCause to Throwable assertions to check the content of a root cause. (Jack Gough)

  • Add isEqualTo(String string, Objects…​ param) to String assertion. (Dmitrii Priporov)

  • Add assertThatObject/thenObject to force Object assertion. (Arvid Heise)

  • Add JUnit5 to AssertJ assertions migration script for osx. (Tomek Kaczanowski)

Improvements

  • Add stack trace of original exception to catchThrowableOfType. (Sam Smyth)

  • anySatisfy and noneSatisfy now reports all failing elements. (Erhard Pointl)

  • ElementsShouldSatisfy now uses the configured Representation to format objects.

  • ZipSatisfyError now uses the configured Representation to format objects. (Jeff Walker)

  • AssertJ Double and Float comparators now support Infinity. (Alexandre de Champeaux)

  • Throw AssertionFailedError instead of AssertionError in some String assertions to allow IDEs to show actual vs expected visual differences. (Yaroslav Mitsynskiy)

  • Optional hasValue/contains assertions throws AssertionFailedError to allow IDEs to show actual vs expected visual differences.

  • Annotate Assertions and Assumptions classes with @CheckReturnValue and annotate methods to exclude from checking with @CanIgnoreReturnValue. (Pascal Schumacher)

  • The error message of allSatisfy(BiConsumer) Map assertion now reports all failing entries instead of the first one. (Stefan Mandel)

  • Add missing @Since annotations. (Erhard Pointl)

  • Get rid of Arguments usage when possible in unit tests. (Erhard Pointl)

  • Unit tests code cleanup and better use of JUnit 5. (Erhard Pointl, Pascal Schumacher and Jack Gough)

  • Update to JUnit 5.4.0. (Erhard Pointl)

  • Update to opentest4j to 1.1.1. (Erhard Pointl)

  • Update to Byte Buddy 1.9.10. (Pascal Schumacher)

  • Update Maven version to and the Maven wrapper. (Thomas Traude, Jeremy Landis)

  • Do not proxy useComparator method in soft assertions. (Lukáš Křečan)

  • Fix an NPE in ObjectArrays#assertHasOnlyElementsOfType. (Yoann Rodière)

  • Deprecate Extractor in favor of java.util.function.Function. (Filip Hrisafov)

Fixed

  • Use @CanIgnoreReturnValue on Assertions fail* methods to revert the effect of the default @CheckReturnValue annotation. (Erhard Pointl)

  • Fix ElementsShouldSatisfy that failed to handle objects whose string representation contained %.

  • Fix ElementsShouldZipSatisfy that failed to handle objects whose string representation contained %. (Arvid Heise)