1. AssertJ Overview

AssertJ is composed of several modules:

  • A core module to provide assertions for JDK types (String, Iterable, Stream, Path, File, Map…​)

  • A Guava module to provide assertions for Guava types (Multimap, Optional…​)

  • A Joda Time module to provide assertions for Joda Time types (DateTime, LocalDateTime)

  • A Neo4J module to provide assertions for Neo4J types (Path, Node, Relationship…​)

  • A DB module to provide assertions for relational database types (Table, Row, Column…​)

  • A Swing module provides a simple and intuitive API for functional testing of Swing user interfaces

2. AssertJ Core

The goal of this document is to provide comprehensive reference documentation for programmers writing tests assertions with AssertJ.

2.1. What is AssertJ Core?

AssertJ is a java library providing a rich set of assertions, truly helpful error messages, improves test code readability and is designed to be super easy to use within your favorite IDE.

http://www.javadoc.io/doc/org.assertj/assertj-core/ is the latest version of assertj core javadoc, each assertion is explained, most of them with code examples so be sure to check it if you want to know what a specific assertion does.

Here are a few examples of AssertJ assertions:

// entry point for all assertThat methods and utility methods (e.g. entry)
import static org.assertj.core.api.Assertions.*;

// basic assertions
assertThat(frodo.getName()).isEqualTo("Frodo");
assertThat(frodo).isNotEqualTo(sauron);

// chaining string specific assertions
assertThat(frodo.getName()).startsWith("Fro")
                           .endsWith("do")
                           .isEqualToIgnoringCase("frodo");

// collection specific assertions (there are plenty more)
// in the examples below fellowshipOfTheRing is a List<TolkienCharacter>
assertThat(fellowshipOfTheRing).hasSize(9)
                               .contains(frodo, sam)
                               .doesNotContain(sauron);

// as() is used to describe the test and will be shown before the error message
assertThat(frodo.getAge()).as("check %s's age", frodo.getName()).isEqualTo(33);

// exception assertion, standard style ...
assertThatThrownBy(() -> { throw new Exception("boom!"); }).hasMessage("boom!");
// ... or BDD style
Throwable thrown = catchThrowable(() -> { throw new Exception("boom!"); });
assertThat(thrown).hasMessageContaining("boom");

// using the 'extracting' feature to check fellowshipOfTheRing character's names
assertThat(fellowshipOfTheRing).extracting(TolkienCharacter::getName)
                               .doesNotContain("Sauron", "Elrond");

// extracting multiple values at once grouped in tuples
assertThat(fellowshipOfTheRing).extracting("name", "age", "race.name")
                               .contains(tuple("Boromir", 37, "Man"),
                                         tuple("Sam", 38, "Hobbit"),
                                         tuple("Legolas", 1000, "Elf"));

// filtering a collection before asserting
assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o"))
                               .containsOnly(aragorn, frodo, legolas, boromir);

// combining filtering and extraction (yes we can)
assertThat(fellowshipOfTheRing).filteredOn(character -> character.getName().contains("o"))
                               .containsOnly(aragorn, frodo, legolas, boromir)
                               .extracting(character -> character.getRace().getName())
                               .contains("Hobbit", "Elf", "Man");

// and many more assertions: iterable, stream, array, map, dates, path, file, numbers, predicate, optional ...

2.2. Getting Help

Ask AssertJ related questions on Stack Overflow.

2.3. Contributing to this guide

You are very welcome to suggest or contribute improvements to this guide, that’s one great way to give back to open source projects!

The repository containing the guide is https://github.com/assertj/doc, you can create a new issue, submit a pull request. Et voila!

This guide is written with the awesome asciidoctor which makes it easy to improve.

2.4. AssertJ Core quick start

This guide is for the AssertJ core module.

AssertJ Core 2.x is in maintenance mode, it will only receive bugfixes.

2.4.1. Get assertj-core library

AssertJ Core artifacts are in the Maven central repository.

Supported Java versions

AssertJ Core major versions depend on different Java versions:

  • AssertJ Core 3.x requires Java 8 or higher

  • AssertJ Core 2.x requires Java 7 or higher

Note that AssertJ Core 3.x includes all AssertJ Core 2.x features and adds Java 8 specific ones (like exception assertions with lambdas).

Android support

AssertJ does not officially support Android but is mostly Android compatible:

  • AssertJ Core 3.x is compatible with Android API Level 26+, except for soft assertions and assumptions.

  • AssertJ Core 2.x is Android compatible with Android API Level 26+ and API Level < 26 except for Path assertions.

Maven
<dependency>
  <groupId>org.assertj</groupId>
  <artifactId>assertj-core</artifactId>
  <!-- use 2.9.1 for Java 7 projects -->
  <version>3.16.1</version>
  <scope>test</scope>
</dependency>
Gradle

For Gradle users (using the Maven Central Repository)

testImplementation("org.assertj:assertj-core:3.16.1")

Or version 2.9.1 for Java 7 projects

testImplementation("org.assertj:assertj-core:2.9.1")
Other build tools

Check this page to find the relevant assertj core dependency declaration.

2.4.2. Use Assertions class entry point

The Assertions class is the only class you need to start using AssertJ, it provides all the methods you need.

Alternatively your test class can implement WithAssertions to access the same methods.

One Assertions static import to rule them all …​

import static org.assertj.core.api.Assertions.*;

... or many if you prefer:

import static org.assertj.core.api.Assertions.assertThat;  // main one
import static org.assertj.core.api.Assertions.atIndex; // for List assertions
import static org.assertj.core.api.Assertions.entry;  // for Map assertions
import static org.assertj.core.api.Assertions.tuple; // when extracting several properties at once
import static org.assertj.core.api.Assertions.fail; // use when writing exception tests
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; // idem
import static org.assertj.core.api.Assertions.filter; // for Iterable/Array assertions
import static org.assertj.core.api.Assertions.offset; // for floating number assertions
import static org.assertj.core.api.Assertions.anyOf; // use with Condition
import static org.assertj.core.api.Assertions.contentOf; // use with File assertions
Alternative entry points

AssertJ provides other entry points class, notably the WithAssertions interface and BDDAssertions for BDD style assertions that replace assertThat by then.

WithAssertions example:

import org.assertj.core.api.WithAssertions;

public class WithAssertionsExamples extends AbstractAssertionsExamples implements WithAssertions {

  // the data used are initialized in AbstractAssertionsExamples.

  @Test
  public void withAssertions_examples() {

    // assertThat methods come from WithAssertions - no static import needed
    assertThat(frodo.age).isEqualTo(33);
    assertThat(frodo.getName()).isEqualTo("Frodo").isNotEqualTo("Frodon");

    assertThat(frodo).isIn(fellowshipOfTheRing);
    assertThat(frodo).isIn(sam, frodo, pippin);
    assertThat(sauron).isNotIn(fellowshipOfTheRing);

    assertThat(frodo).matches(p -> p.age > 30 && p.getRace() == HOBBIT);
    assertThat(frodo.age).matches(p -> p > 30);
  }
}

BDDAssertions example:

import static org.assertj.core.api.BDDAssertions.then;

public class BDDAssertionsExamples extends AbstractAssertionsExamples {

  // the data used are initialized in AbstractAssertionsExamples.

  @Test
  public void withAssertions_examples() {

    // then methods come from BDDAssertions.then static
    then(frodo.age).isEqualTo(33);
    then(frodo.getName()).isEqualTo("Frodo").isNotEqualTo("Frodon");

    then(frodo).isIn(fellowshipOfTheRing);
    then(frodo).isIn(sam, frodo, pippin);
    then(sauron).isNotIn(fellowshipOfTheRing);

    then(frodo).matches(p -> p.age > 30 && p.getRace() == HOBBIT);
    then(frodo.age).matches(p -> p > 30);
  }
}
IDE configuration

You can configure your IDE so that when you start typing as and trigger code completion assertThat will show up in the suggested completions.

Eclipse: . Go to : Window > Preferences > Java > Editor > Content Assist > Favorites > New Type . Enter : org.assertj.core.api.Assertions and click OK . Check that you see org.assertj.core.api.Assertions.* in Favorites.

Intellij Idea: No special configuration is needed, just start typing asser and then invoke completion (Ctrl-Space) twice.

2.4.3. Use code completion

Type assertThat followed by the object under test and a dot …​ and any Java IDE code completion will show you all available assertions.

assertThat(objectUnderTest). (1)
1 Use IDE code completion after the dot.

Example for String assertions:

ide completion

2.4.4. Javadoc

http://www.javadoc.io/doc/org.assertj/assertj-core/ is the latest version of assertj core javadoc, each assertion is explained, most of them with code examples so be sure to check it if you want to know what a specific assertion does.

2.5. Core assertions guide

This section describes the assertions provided by AssertJ Core and other useful features to get the best of it.

AssertJ Core javadoc explains each assertions most of them with code examples so be sure to check it if you want to know what a specific assertion does.

2.5.1. A simple example

Let’s start with a simple example showing a few important things.

import static org.assertj.core.api.Assertions.assertThat; (1)

import org.junit.jupiter.api.Test;

public class SimpleAssertionsExample {

  @Test
  void a_few_simple_assertions() {
    assertThat("The Lord of the Rings").isNotNull()  (2) (3)
                                       .startsWith("The") (4)
                                       .contains("Lord") (4)
                                       .endsWith("Rings"); (4)
  }

}
1 Statically import org.assertj.core.api.Assertions.assertThat
2 Pass the object under test as the sole assertThat() parameter
3 Use code completion to discover and call assertions
4 Chain as many assertions as you need

Except for isNotNull which is a base assertion, the other assertions are String specific as our object under test is a String.

2.5.2. Supported type assertions

AssertJ provides assertions specific to the object under test type, the following sections list the supported types grouped by categories.

The provided assertions for each of these types are documented later on.

Common types

BigDecimal

BigInteger

CharSequence

Class

Date

File

Future / CompletableFuture

InputStream

Iterable (including any kind of Collection)

Iterator

List

Map

Object

Object[]

  • Optional

  • OptionalInt / OptionalLong / OptionalDouble

Path

Predicate

Stream

String

Throwable / Exception

Primitive types

Primitive types and their wrapper:

  • short / Short

  • int / Integer

  • long / Long

  • byte / Byte

  • char / Character

  • float / Float

  • double / Double

Primitive type arrays:

  • short[]

  • int[]

  • long[]

  • byte[]

  • char[]

  • float[]

  • double[]

Java 8 Temporal types

Instant

LocalDate

LocalDateTime

LocalTime

OffsetDateTime

OffsetTime

Atomic types

Atomic basic types:

  • AtomicInteger

  • AtomicLong

  • AtomicBoolean

Atomic array types:

  • AtomicIntegerArray

  • AtomicLongArray

Atomic reference types:

  • AtomicMarkableReference

  • AtomicStampedReferenceAssert

Atomic updater types:

  • AtomicIntegerFieldUpdater

  • AtomicLongFieldUpdater

  • AtomicReferenceFieldUpdater

Adder types:

  • LongAdder

2.5.3. Assertion description

It is often valuable to describe the assertion performed, especially for boolean assertions where the default error message just complains that it got false instead of true (or vice versa).

You can set such a description with as(String description, Object…​ args) but remember to do it before calling the assertion otherwise it is simply ignored as a failing assertion breaks the chained calls.

Example of a failing assertion with a description:

TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, Race.HOBBIT);

// failing assertion, remember to call as() before the assertion!
assertThat(frodo.getAge()).as("check %s's age", frodo.getName())
                          .isEqualTo(100);

The error message starts with the given description in [] :

[check Frodo's age] expected:<100> but was:<33>

2.5.4. Overriding error message

AssertJ tries its best to give helpful error messages but you always change it with overridingErrorMessage() or withFailMessage().

Example with this failing test:

TolkienCharacter frodo = new TolkienCharacter("Frodo", 33, Race.HOBBIT);
TolkienCharacter sam = new TolkienCharacter("Sam", 38, Race.HOBBIT);
// failing assertion, remember to call withFailMessage/overridingErrorMessage before the assertion!
assertThat(frodo.getAge()).withFailMessage("should be %s", frodo)
                          .isEqualTo(sam);

The error message is:

java.lang.AssertionError: should be TolkienCharacter [name=Frodo, age=33, race=HOBBIT]

2.5.5. Avoiding incorrect usage

There are a few things to keep in mind when using AssertJ to avoid misusing it.

Forgetting to call an assertion

The main trap is to pass the object under to test to assertThat() and forget to call an assertion afterward. This misuse can be detected by SpotBugs or Findbugs thanks to the @CheckReturnValue annotation on all assertThat() methods.

Here’s what it looks like in SpotBugs:

SpotBugs detecting AssertJ invalid usage
Figure 1. SpotBugs detecting AssertJ invalid usage

The following examples show incorrect AssertJ API usage to avoid!

Bad

// DON'T DO THIS ! It does not assert anything
assertThat(actual.equals(expected));

Good

// DO THIS:
assertThat(actual).isEqualTo(expected);

// OR THIS (less classy but ok):
assertThat(actual.equals(expected)).isTrue();

Bad

// DON'T DO THIS ! It does not assert anything and passes
assertThat(1 == 2);

Good

// DO THIS: (fails as expected)
assertThat(1).isEqualTo(2);

// OR THIS (less classy but ok):
assertThat(1 == 2).isTrue();
Calling as() after the assertion

Describing an assertion must be done before calling the assertion otherwise it is ignored as a failing assertion breaks will prevent the call to as().

Bad

// DON'T DO THIS ! as/describedAs have no effect after the assertion
assertThat(actual).isEqualTo(expected).as("description");
assertThat(actual).isEqualTo(expected).describedAs("description");

Good

// DO THIS: use as/describedAs before the assertion
assertThat(actual).as("description").isEqualTo(expected);
assertThat(actual).describedAs("description").isEqualTo(expected);
Calling withFailMessage/overridingErrorMessage after the assertion

Setting an error message must be done before calling the assertion otherwise it is ignored as a failing assertion breaks will prevent the call to withFailMessage() / overridingErrorMessage().

Bad

// DON'T DO THIS ! overridingErrorMessage/withFailMessage have no effect after the assertion
assertThat(actual).isEqualTo(expected).overridingErrorMessage("custom error message");
assertThat(actual).isEqualTo(expected).withFailMessage("custom error message");

Good

// DO THIS: use overridingErrorMessage/withFailMessage before the assertion
assertThat(actual).overridingErrorMessage("custom error message").isEqualTo(expected);
assertThat(actual).withFailMessage("custom error message").isEqualTo(expected);
Setting a comparator after the assertion

Setting comparators must be done before calling the assertion otherwise it is ignored as a failing assertion breaks will prevent the call to usingComparator() / usingElementComparator().

Bad

// DON'T DO THIS ! Comparator is not used
assertThat(actual).isEqualTo(expected).usingComparator(new CustomComparator());

Good

// DO THIS:
assertThat(actual).usingComparator(new CustomComparator()).isEqualTo("a");

2.5.6. Configuring AssertJ

This section describes the different ways to configure AssertJ, either by setting configuration properties individually or globally using the Configuration class.

To be effective the configuration changes must be applied before the tests are executed, depending on the scope of the tests this means different things:

  • For a single test: change the configuration in the test and revert it in the @AfterEach method (JUnit 5).

  • For all tests in a class: change the configuration in the @BeforeAll method and revert the changes in the @AfterAll method (JUnit 5).

  • To change the configuration before any tests, you can use these options:

Configuring single properties

The Assertions class provides static methods to change each configuration properties.

Assertions.setAllowComparingPrivateFields(true);
Assertions.setAllowExtractingPrivateFields(false);
Assertions.setExtractBareNamePropertyMethods(false);
Assertions.setLenientDateParsing(true);
Assertions.setMaxElementsForPrinting(100);
Assertions.setMaxLengthForSingleLineDescription(250);
Assertions.setRemoveAssertJRelatedElementsFromStackTrace(true);
Assertions.useRepresentation(myRepresentation);
Assertions.registerCustomDateFormat(myCustomDateFormat);
Representation

This property allows you to register a Representation to control the way AssertJ formats the different types displayed in the assertion error messages. Consult the Controlling type formatting chapter for details.

Defaults to StandardRepresentation.

AllowComparingPrivateFields

Globally sets whether the use of private fields is allowed for field/property by field/property comparison. Defaults to true.

AllowExtractingPrivateFields

Globally sets whether the AssertJ extracting capability should be allowed to extract private fields. Defaults to true.

ExtractBareNamePropertyMethods

Globally sets whether the AssertJ extracting capability considers bare-named property methods like String name(). Defaults to true.

LenientDateParsing

Specify whether or not date/time parsing is to be lenient for AssertJ default date formats. With lenient parsing, the parser may use heuristics to interpret inputs that do not precisely match this object’s format. With strict parsing, inputs must match this object’s format.

Custom DateFormat

In addition to the default date formats, you can register some custom ones that AssertJ will use in date assertions (see also Assertions.registerCustomDateFormat).

Note that custom date formats take precedence over default ones.

MaxElementsForPrinting

In error messages, sets the threshold for how many elements from one iterable/array/map will be included in the in the description. Defaults to 1000.

The point of this property is to avoid printing iterable/array/map with too many elements in error messages.

MaxLengthForSingleLineDescription

In error messages, sets the threshold when iterable/array formatting will be on one line (if their String description is less than this parameter) or it will be formatted with one element per line. Defaults to 80.

Example:

String[] greatBooks = array("A Game of Thrones", "The Lord of the Rings", "Assassin's Apprentice");

this array is formatted on one line as its length < 80:

["A Game of Thrones", "The Lord of the Rings", "Assassin's Apprentice"]

Whereas this array …​

String[] greatBooks = array("A Game of Thrones", "The Lord of the Rings", "Assassin's Apprentice", "Guards! Guards! (Discworld)");

... is formatted on multiple lines with one element per line:

["A Game of Thrones",
 "The Lord of the Rings",
 "Assassin's Apprentice",
 "Guards! Guards! (Discworld)"]
RemoveAssertJRelatedElementsFromStackTrace

Sets whether the elements related to AssertJ are removed from assertion errors stack trace. Defaults to true.

AssertJ Configuration

Since 3.13.0, AssertJ exposes a org.assertj.core.configuration.Configuration object providing access to all AssertJ globally configurable properties.

You can create an instance of org.assertj.core.configuration.Configuration and change indivual properties through setters or create your own custom configuration by inheriting from it and overriding the methods to change the default behavior as in the CustomConfiguration example below.

Your configuration will be effective once you call Configuration.apply() or Configuration.applyAndDisplay().

Example:

Configuration configuration = new Configuration();

configuration.setBareNamePropertyExtraction(false);
configuration.setComparingPrivateFields(false);
configuration.setExtractingPrivateFields(false);
configuration.setLenientDateParsing(true);
configuration.setMaxElementsForPrinting(1001);
configuration.setMaxLengthForSingleLineDescription(81);
configuration.setRemoveAssertJRelatedElementsFromStackTrace(false);

// don't forget to apply it!
configuration.applyAndDisplay();

Printing the above configuration produces the following output:

Applying configuration org.assertj.core.configuration.Configuration
- representation .................................. = BinaryRepresentation
- comparingPrivateFieldsEnabled ................... = false
- extractingPrivateFieldsEnabled .................. = true
- bareNamePropertyExtractionEnabled ............... = false
- lenientDateParsingEnabled ....................... = true
- additionnal date formats ........................ = [yyyy_MM_dd, yyyy|MM|dd]
- maxLengthForSingleLineDescription ............... = 150
- maxElementsForPrinting .......................... = 2000
- removeAssertJRelatedElementsFromStackTraceEnabled = true
Automagic configuration discovery

This section describes a way to register an AssertJ Configuration without using any test framework hooks like BeforeAllCallback.

Follow the steps below to register your Configuration as an SPI:

  • Create your own configuration inheriting from org.assertj.core.configuration.Configuration

  • Create a file named org.assertj.core.configuration.Configuration in a META-INF/services directory

  • Make sure META-INF/services/ is in the runtime classpath, usually putting it in src/test/resources will do.

  • Put the fully qualified class name of your Configuration in services/org.assertj.core.configuration.Configuration.

This is all you have to do, AssertJ will pick up the Configuration automatically and display it at the first interaction with AssertJ.

Here’s an example of a custom configuration class:

package example.core;

import static org.assertj.core.presentation.BinaryRepresentation.BINARY_REPRESENTATION;
import static org.assertj.core.util.Lists.list;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;

import org.assertj.core.configuration.Configuration;
import org.assertj.core.presentation.Representation;

class CustomConfiguration extends Configuration {

  private static final SimpleDateFormat DATE_FORMAT1 = new SimpleDateFormat("yyyy_MM_dd");
  private static final SimpleDateFormat DATE_FORMAT2 = new SimpleDateFormat("yyyy|MM|dd");

  // we keep the default behavior for extractingPrivateFieldsEnabled since it is not overridden

  @Override
  public Representation representation() {
    return BINARY_REPRESENTATION;
  }

  @Override
  public boolean bareNamePropertyExtractionEnabled() {
    return false;
  }

  @Override
  public boolean comparingPrivateFieldsEnabled() {
    return false;
  }

  @Override
  public boolean lenientDateParsingEnabled() {
    return true;
  }

  @Override
  public List<DateFormat> additionalDateFormats() {
    return list(DATE_FORMAT1, DATE_FORMAT2);
  }

  @Override
  public int maxElementsForPrinting() {
    return 2000;
  }

  @Override
  public int maxLengthForSingleLineDescription() {
    return 150;
  }
}

With this custom configuration, the content of META-INF/services/org.assertj.core.configuration.Configuration must be:

example.core.CustomConfiguration

Printing the CustomConfiguration shows:

Applying configuration example.core.CustomConfiguration
- representation .................................. = BinaryRepresentation
- comparingPrivateFieldsEnabled ................... = false
- extractingPrivateFieldsEnabled .................. = true
- bareNamePropertyExtractionEnabled ............... = false
- lenientDateParsingEnabled ....................... = true
- additionnal date formats ........................ = [yyyy_MM_dd, yyyy|MM|dd]
- maxLengthForSingleLineDescription ............... = 150
- maxElementsForPrinting .......................... = 2000
- removeAssertJRelatedElementsFromStackTraceEnabled = true

2.5.7. Controlling type formatting

Assertions error messages use a Representation to format the different types involved. There are multiple ways of registering a custom Representation for assertions:

Let’s go over these different options with a custom Representation.

Creating a custom Representation

An example of a custom Representation:

// dummy class
private class Example {}

public class CustomRepresentation extends StandardRepresentation { (1)

  // override fallbackToStringOf to handle Example formatting
  @Override
  public String fallbackToStringOf(Object o) { (2)
    if (o instanceof Example) return "Example";
    // fallback to default formatting.
    return super.fallbackToStringOf(o);
  }

  // override a predefined type formatting : String
  @Override
  protected String toStringOf(String str) { (3)
    return "$" + str + "$";
  }
}
1 Extends org.assertj.core.presentation.StandardRepresentation to get AssertJ default representation.
2 Override fallbackToStringOf and handle your specific types before falling back to the default formatting.
3 Change a predefined type formatting by overriding the toStringOf method that takes it as a parameter.

Let’s see the above custom representation in action when representing Example or String instances.

This assertion fails …​

assertThat(new Example()).isNull();

…​with the following error:

expected:<[null]> but was:<[Example]>

This one fails …​

// this one fails ...
assertThat("foo").startsWith("bar");

…​with the following error:

Expecting:
  <$foo$>
to start with:
  <$bar$>
Changing the default global scope custom representation

You only have to register CustomRepresentation once but need to do it before executing any tests, for the tests executed before that, AssertJ will use the default representation.

// to call before executing tests
Assertions.useRepresentation(new CustomRepresentation());

Consider writing a JUnit 5 extension implementing BeforeAllCallback to make sure the representation is set once for all before any test is executed.

Per assertion scope custom representation

Follow this approach if you want to use a specific representation for a single assertion only.

Example with the failing assertions used before:

Representation customRepresentation = new CustomRepresentation();

// this assertion fails ...
assertThat(new Example()).withRepresentation(customRepresentation)
                         .isNull();

assertThat("foo").withRepresentation(customRepresentation)
                 .startsWith("bar");

2.5.8. Common assertions

This section describes the assertions common to all types.

2.5.9. Object assertions (TODO)

TODO

2.5.10. String/CharSequence assertions

This section describes all the available assertions for CharSequence (including String, StringBuilder, StringBuffer, …​):

The javadoc for CharSequence assertions is available here.

2.5.11. Iterable/Collection/array assertions (WIP)

TODO

byte[] specific assertions

This section covers assertions to byte array.

2.5.12. Exception assertions

This chapter answers the question: how to assert that an exception has been thrown and check that it is the expected one?

If you use java 8 or later versions, check the Java 8 section which heavily uses lambdas. If you use java 7, check the Java 7 section.

All the available assertions are described in the throwable assertions reference section.

In this chapter the term exception is used interchangeably with throwable.

With Java 8 (AssertJ 3.x)

Testing assertions in Java 8 is elegant, thanks to lambdas! AssertJ provides different style options:

where ThrowingCallable is a functional interface which can be expressed as a lambda.

BDD style

BDD aficionados can separate WHEN and THEN steps by using catchThrowable(ThrowingCallable) to capture the Throwable and then perform assertions.

Example:

@Test
public void bdd_exception_assertion_example() {
   // GIVEN
   String[] names = { "Pier ", "Pol", "Jak" };
   // WHEN
   Throwable thrown = catchThrowable(() -> System.out.println(names[9]));
   // THEN
   assertThat(thrown).isInstanceOf(ArrayIndexOutOfBoundsException.class)
                     .hasMessageContaining("9");
}
BDD style on specific Throwable type

This is a variation of catchThrowable where the caught exception type is specified, allowing to check the custom exception fields/properties.

Example:

class TextException extends Exception {
   int line;
   int column;

   public TextException(String msg, int line, int column) {
     super(msg);
     this.line = line;
     this.column = column;
   }
 }

 TextException textException = catchThrowableOfType(() -> { throw new TextException("boom!", 1, 5); },
                                                    TextException.class);
 // assertions succeed
 assertThat(textException).hasMessageContaining("boom");
 assertThat(textException.line).isEqualTo(1);
 assertThat(textException.column).isEqualTo(5);

 // succeeds as catchThrowableOfType returns null when the code does not thrown any exceptions
 assertThat(catchThrowableOfType(() -> {}, Exception.class)).isNull();

 // fails as TextException is not a RuntimeException
 catchThrowableOfType(() -> { throw new TextException("boom!", 1, 5); }, RuntimeException.class);
Exception testing with assertThatThrownBy

Starts with assertThatThrownBy(ThrowingCallable) to capture and then assert on the thrown Throwable.

Note that if the provided ThrowingCallable does not raise an exception, an assertion error is immediately thrown.

Example:

@Test
public void exception_assertion_example() {
   assertThatThrownBy(() -> { throw new Exception("boom!"); }).isInstanceOf(Exception.class)
                                                              .hasMessageContaining("boom");
}
Exception testing with assertThatExceptionOfType

assertThatExceptionOfType is an alternative syntax that some people find more natural.

Note that if the code under test ThrowingCallable does not raise an exception, an assertion error is immediately thrown.

@Test
public void exception_assertion_example() {
   assertThatExceptionOfType(IOException.class).isThrownBy(() -> { throw new IOException("boom!"); })
                                               .withMessage("%s!", "boom")
                                               .withMessageContaining("boom")
                                               .withNoCause();
}

This latter syntax has been enriched for common exceptions:

  • assertThatNullPointerException

  • assertThatIllegalArgumentException

  • assertThatIllegalStateException

  • assertThatIOException

The previous test can be rewritten as:

@Test
public void exception_assertion_example() {
   assertThatIOException().isThrownBy(() -> { throw new IOException("boom!"); })
                          .withMessage("%s!", "boom")
                          .withMessageContaining("boom")
                          .withNoCause();
}
Testing that no exception is thrown

You can test that a piece of code does not throw any exception with:

assertThatCode(() -> {
  // code that should NOT throw an exception
  ...
}).doesNotThrowAnyException();
Throwable reference assertions reference (WIP)

This section describes all the available assertions for throwables:

The javadoc for throwable assertions is available here.

Checking the throwable message

There are two ways to check the exception message: hasMessage(String message) and hasMessage(String message, Object…​ parameters), the latter allows you to build the expected message as String.format.

Examples:

Throwable invalidArgException = new IllegalArgumentException("foo is not a valid input");
Throwable throwable = new Throwable(invalidArgException);

// This assertion succeeds:
assertThat(throwable).hasMessage("foo is not a valid input");
assertThat(throwable).hasMessage("%s is not a valid input", "foo");

// These assertions fail:
assertThat(throwable).hasMessage("bar is not a valid input");
assertThat(throwable).hasMessage("%s is not a valid input", "bar");
Checking cause and root cause

It is possible to assert on the cause and/or root cause properties of an exception by using getCause() and getRootCause().
These methods change the object under test from the current throwable to its cause or root cause.

Examples:

NullPointerException rootCause = new NullPointerException("root cause message");
IllegalArgumentException cause = new IllegalArgumentException("cause message", rootCause);
Throwable throwable = new Throwable("top level", cause);

// These assertions succeed:
assertThat(throwable).hasMessage("top level")
                     .getCause()
                     // all further chaining is against throwable's cause
                     .hasMessage("cause message");

assertThat(throwable).hasMessage("top level")
                     .getRootCause()
                     // all further chaining is against throwable's root cause
                     .hasMessage("root cause message");

// This assertion fails:
assertThat(throwable).hasMessage("top level")
                     .getCause()
                     .hasMessage("some other message");

The same is also possible using assertThatExceptionOfType as in the following example checking that the throwable is a RuntimeException with "boom!" for its cause message.

assertThatExceptionOfType(RuntimeException.class)
         .isThrownBy(() -> { throw new RuntimeException(new IllegalArgumentException("boom!")); })
         .havingCause()
         .withMessage("boom!");

Similar example with havingRootCause:

assertThatExceptionOfType(RuntimeException.class)
         .isThrownBy(() -> { throw new RuntimeException(new IllegalArgumentException(new NullPointerException("root error"))); })
         .havingRootCause()
         .withMessage("root error");
With Java 7 (AssertJ 2.x)

Asserting on exceptions is not as nice compared to the Java 8 way, this is how you would do it in AssertJ 2.x :

  1. Put the code that should throw the exception in a try-catch.

  2. Call fail method immediately after, so that the test fails if the exception is not thrown.

  3. Make assertions on the caught exception.

Note that fail method can be statically imported from Assertions class.

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
// ... code omitted for brevity

assertThat(fellowshipOfTheRing).hasSize(9);

// here's the typical pattern to use Fail :
try {
  fellowshipOfTheRing.get(9); // argggl !
  // we should not arrive here => use fail to expresses that
  // if IndexOutOfBoundsException was not thrown, test would fail the specified message
  fail("IndexOutOfBoundsException expected because fellowshipOfTheRing has only 9 elements");
} catch (IndexOutOfBoundsException e) {
  assertThat(e).hasMessage("Index: 9, Size: 9");
}

// Warning : don't catch Throwable as it would also catch the AssertionError thrown by fail method

// another way to do the same thing
try {
  fellowshipOfTheRing.get(9); // argggl !
  // if IndexOutOfBoundsException was not thrown, test would fail with message :
  // "Expected IndexOutOfBoundsException to be thrown"
  failBecauseExceptionWasNotThrown(IndexOutOfBoundsException.class);
} catch (IndexOutOfBoundsException e) {
  assertThat(e).hasMessage("Index: 9, Size: 9");
}

2.5.13. Field by field recursive comparison

Since 3.12.0 AssertJ Core provides a new fluent recursive comparison API for Object assertions, it is meant to replace isEqualToComparingFieldByFieldRecursively with more capabilities including:

The recursive comparison mode starts after calling usingRecursiveComparison(). By default it uses equals methods of classes that have overriden it, this behavior can be changed forcing recursive comparison on these classes (see this section).

Here’s a simple example to give an idea of what it can do:

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

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

public static class Address {
  int number;
  String street;
}

Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.ownedSince = new Date(123);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;

Person sherlock2 = new Person("Sherlock", 1.80);
sherlock2.home.ownedSince = new Date(123);
sherlock2.home.address.street = "Baker Street";
sherlock2.home.address.number = 221;

// assertion succeeds as the data of both objects are the same.
assertThat(sherlock).usingRecursiveComparison()
                    .isEqualTo(sherlock2);

// assertion fails as Person equals only compares references.
assertThat(sherlock).isEqualTo(sherlock2);

The comparison is not symmetrical since it is limited to actual’s fields, the algorithm gather actual’s fields and then compare them to the corresponding expected’s fields. It is then possible for the expected object to have more fields than actual which can be handy when comparing a base type to a subtype with additional fields.

Strict or lenient comparison

By default the objects to compare can be of different types but must have the same properties/fields. For example if object under test has a work field of type Address, the expected object to compare the object under test to must also have one but it can of a different type like AddressDto.

It is possible to enforce strict type checking by calling withStrictTypeChecking() and make the comparison fail whenever the compared objects or their fields are not compatible. Compatible means that the expected object/field types are the same or a subtype of actual/field types, for example if actual is an Animal and expected a Dog, they will be compared field by field in strict type checking mode.

public class Person {
  String name;
  double height;
  Person bestFriend;
}

Person sherlock = new Person("Sherlock", 1.80);
sherlock.bestFriend = new Person("Watson", 1.70);

Person sherlockClone = new Person("Sherlock", 1.80);
sherlockClone.bestFriend = new Person("Watson", 1.70);

// assertion succeeds as sherlock and sherlockClone have the same data and types
assertThat(sherlock).usingRecursiveComparison()
                    .withStrictTypeChecking()
                    .isEqualTo(sherlockClone);

// Let's now define a data structure similar to Person

public class PersonDTO {
  String name;
  double height;
  PersonDTO bestFriend;
}

PersonDTO sherlockDto = new PersonDTO("Sherlock", 1.80);
sherlockDto.bestFriend = new PersonDTO("Watson", 1.70);

// assertion fails as Person and PersonDTO are not compatible even though they have the same data
assertThat(sherlock).usingRecursiveComparison()
                    .withStrictTypeChecking()
                    .isEqualTo(noName);

// Let's define a subclass of Person

public class Detective extends Person {
  boolean busy;
}

Detective detectiveSherlock = new Detective("Sherlock", 1.80);
detectiveSherlock.bestFriend = new Person("Watson", 1.70);
detectiveSherlock.busy = true;

// assertion succeeds as Detective inherits from Person and
// only Person's fields are included into the comparison.
assertThat(sherlock).usingRecursiveComparison()
                    .withStrictTypeChecking()
                    .isEqualTo(detectiveSherlock);
Ignoring fields in the comparison

It is possible to ignore fields of the object under test in the comparison, this is can be useful when a field has a generated value (like the current time) or is simply not relevant to compare.

There are a few ways to specify the fields to ignore:

  • directly with ignoringFields(String…​ fieldsToIgnore)

  • by regexes with ignoringFieldsMatchingRegexes(String…​ regexes)

  • by types with ignoringFieldsOfTypes(Class…​ typesToIgnore)

Nested fields can be specified like this: home.address.street

It is also possible to ignore the the object under test with ignoringActualNullFields().

Examples

Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;

// strangely moriarty and sherlock have the same height!
Person moriarty = new Person("Moriarty", 1.80);
moriarty.home.address.street = "Crime Street";
moriarty.home.address.number = 221;

// assertion succeeds as name and home.address.street fields are ignored in the comparison
assertThat(sherlock).usingRecursiveComparison()
                    .ignoringFields("name", "home.address.street")
                    .isEqualTo(moriarty);

// assertion succeeds as once a field is ignored, its subfields are too
assertThat(sherlock).usingRecursiveComparison()
                    .ignoringFields("name", "home")
                    .isEqualTo(moriarty);

// ignoring fields matching regexes: name and home match .*me
assertThat(sherlock).usingRecursiveComparison()
                    .ignoringFieldsMatchingRegexes(".*me")
                    .isEqualTo(moriarty);

// ignoring null fields example:
sherlock.name = null;
sherlock.home.address.street = null;
assertThat(sherlock).usingRecursiveComparison()
                    .ignoringActualNullFields()
                    .isEqualTo(moriarty);

// ignore height and address fields by type:
Person tallSherlock = new Person("sherlock", 2.10);
tallSherlock.home.address.street = "Long Baker Street";
tallSherlock.home.address.number = 222;
assertThat(sherlock).usingRecursiveComparison()
                    .ignoringFieldsOfTypes(double.class, Address.class)
                    .isEqualTo(tallSherlock);
Ignoring overridden equals

By default the recursive comparison uses overridden equals methods to compare fields, it is possible to change that behavior and force a recursive comparison by calling:

  • ignoringOverriddenEqualsForTypes(Class…​) Any fields of these classes are compared recursively

  • ignoringOverriddenEqualsForFields(String…​) Any given fields are compared recursively

  • ignoringOverriddenEqualsForFieldsMatchingRegexes(String…​) Any fields matching one of these regexes are compared recursively

  • ignoringAllOverriddenEquals() except for java types, all fields are compared field by field recursively

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;

  // only compares number, ouch!
  @Override
  public boolean equals(final Object other) {
    if (!(other instanceof Address)) return false;
    Address castOther = (Address) other;
    return Objects.equals(number, castOther.number);
  }
}

Person sherlock = new Person("Sherlock", 1.80);
sherlock.home.address.street = "Baker Street";
sherlock.home.address.number = 221;

Person sherlock2 = new Person("Sherlock", 1.80);
sherlock2.home.address.street = "Butcher Street";
sherlock2.home.address.number = 221;

// assertion succeeds but that's not what we expected since the home.address.street fields differ
// but the equals implementation in Address does not compare them.
assertThat(sherlock).usingRecursiveComparison()
                    .isEqualTo(sherlock2);

// to avoid the previous issue, we force a recursive comparison on the Address type
// now this assertion fails as expected since the home.address.street fields differ.
assertThat(sherlock).usingRecursiveComparison()
                    .ignoringOverriddenEqualsForTypes(Address.class)
                    .isEqualTo(sherlock2);
Ignoring all expected null fields

By using ignoringExpectedNullFields() the recursive comparison will exclude from the comparison any null fields in the expected object.
One use case for that is when the object under test have fields with values hard to predict (id, timestamp, …​), with this feature you simply build the expected object with null values values for these fields and they won’t 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 noName = new Person(null, 1.80);
noName.home.address.street = null;
noName.home.address.number = 221;

// assertion succeeds as name and home.address.street fields are ignored in the comparison
assertThat(sherlock).usingRecursiveComparison()
                    .ignoringExpectedNullFields()
                    .isEqualTo(noName);

// assertion fails as name and home.address.street fields are populated for sherlock but not for noName.
assertThat(noName).usingRecursiveComparison()
                  .ignoringExpectedNullFields()
                  .isEqualTo(sherlock);
Ignoring all actual empty optional fields

ignoringActualEmptyOptionalFields() makes the recursive comparison to ignore all actual empty optional fields (including Optional, OptionalInt, OptionalLong and OptionalDouble).
Note that the expected object empty optional fields are not ignored, this only applies to actual’s fields.

public class Person {
  String name;
  OptionalInt age;
  OptionalLong id;
  OptionalDouble height;
  Home home = new Home();
}

public class Home {
  String address;
  Optional<String> phone;
}

Person homerWithoutDetails = new Person("Homer Simpson");
homerWithoutDetails.home.address.street = "Evergreen Terrace";
homerWithoutDetails.home.address.number = 742;
homerWithoutDetails.home.phone = Optional.empty();
homerWithoutDetails.age = OptionalInt.empty();
homerWithoutDetails.id = OptionalLong.empty();
homerWithoutDetails.height = OptionalDouble.empty();

Person homerWithDetails = new Person("Homer Simpson");
homerWithDetails.home.address.street = "Evergreen Terrace";
homerWithDetails.home.address.number = 742;
homerWithDetails.home.phone = Optional.of("(939) 555-0113");
homerWithDetails.age = OptionalInt.of(39);
homerWithDetails.id = OptionalLong.of(123456);
homerWithDetails.height = OptionalDouble.of(1.83);

// assertion succeeds as phone is ignored in the comparison
assertThat(homerWithoutDetails).usingRecursiveComparison()
                               .ignoringActualEmptyOptionalFields()
                               .isEqualTo(homerWithDetails);

// assertion fails as phone, age, id and height are not ignored and are populated for homerWithDetails but not for homerWithoutDetails.
assertThat(homerWithDetails).usingRecursiveComparison()
                            .ignoringActualEmptyOptionalFields()
                            .isEqualTo(homerWithoutDetails);
Comparators used in the comparison

By default floats are compared with a precision of 1.0E-6 and doubles with 1.0E-15.

You can specify a custom comparator per (nested) fields or type with the methods below (but before calling isEqualTo otherwise this has no effect!):

  • withComparatorForFields(Comparator, String…​) for one or multiple fields

  • withComparatorForType(Comparator, Class) for a given type

Examples:

public class TolkienCharacter {
  String name;
  double height;
}

TolkienCharacter frodo = new TolkienCharacter("Frodo", 1.2);
TolkienCharacter tallerFrodo = new TolkienCharacter("Frodo", 1.3);
TolkienCharacter reallyTallFrodo = new TolkienCharacter("Frodo", 1.9);

Comparator<Double> closeEnough = (d1, d2) -> Math.abs(d1 - d2) <= 0.5 ? 0 : 1;

// assertion succeeds
assertThat(frodo).usingRecursiveComparison()
                 .withComparatorForFields(closeEnough, "height")
                 .isEqualTo(tallerFrodo);

assertThat(frodo).usingRecursiveComparison()
                 .withComparatorForType(closeEnough, Double.class)
                 .isEqualTo(tallerFrodo);


// assertions fail
assertThat(frodo).usingRecursiveComparison()
                 .withComparatorForFields(closeEnough, "height")
                 .isEqualTo(reallyTallFrodo);

assertThat(frodo).usingRecursiveComparison()
                 .withComparatorForType(closeEnough, Double.class)
                 .isEqualTo(reallyTallFrodo);

2.5.14. Soft assertions

With soft assertions AssertJ collects all assertion errors instead of stopping at the first one.

This is especially useful for long tests like end to end tests as we can fix all reported errors at once and avoid multiple failing runs.

Since soft assertions don’t fail at the first error, you need to tell AssertJ when to report the captured assertion errors, there are different ways of doing so:

Soft assertions comes with a BDD flavor where assertThat is replaced by then.

If you have created your own custom Soft assertions it is possible to combine them all in a single soft assertions entry point.

Let’s see first how to use soft assertions requiring an explicit call to assertAll(), the other approaches that don’t require this explicitit call are described in the subsequent sections.

Example:

@Test
void basic_soft_assertions_example() {
  SoftAssertions softly = new SoftAssertions(); (1)

  softly.assertThat("George Martin").as("great authors").isEqualTo("JRR Tolkien");  (2)
  softly.assertThat(42).as("response to Everything").isGreaterThan(100); (2)
  softly.assertThat("Gandalf").isEqualTo("Sauron"); (2)

  // Don't forget to call assertAll() otherwise no assertion errors are reported!
  softly.assertAll(); (3)
}
1 Build a SoftAssertions instance to record all assertion errors
2 Use softly.assertThat instead of the usual assertThat methods
3 Don’t forget to call assertAll() to report all assertion errors!

The previous test fails with the message below reporting all the errors:

Multiple Failures (3 failures)
-- failure 1 --
[great authors]
Expecting:
 <"George Martin">
to be equal to:
 <"JRR Tolkien">
but was not.
-- failure 2 --
[response to Everything]
Expecting:
 <42>
to be greater than:
 <100>
-- failure 3 --
Expecting:
 <"gandalf">
to be equal to:
 <"sauron">
but was not.
BDD Soft assertions

BDD aficionados can use BDD soft assertions where assertThat is replaced by then.

Example:

@Test
void basic_bdd_soft_assertions_example() {
  BDDSoftAssertions softly = new BDDSoftAssertions();

  softly.then("George Martin").as("great authors").isEqualTo("JRR Tolkien");
  softly.then(42).as("response to Everything").isGreaterThan(100);
  softly.then("Gandalf").isEqualTo("Sauron");

  // Don't forget to call assertAll() otherwise no assertion errors are reported!
  softly.assertAll();
}

There are BDD soft assertions versions for the different soft assertions approaches:

  • AutoCloseableBDDSoftAssertions

  • Using JUnitBDDSoftAssertions that takes care of calling assertAll() after each tests

  • Using a JUnit 5 extension that takes care of calling assertAll() after each tests

JUnit 4 Soft assertions rule

The JUnit rule provided by AssertJ takes care of calling assertAll() at the end of each tests.

Example:

@Rule
public final JUnitSoftAssertions softly = new JUnitSoftAssertions();

@Test
void junit4_soft_assertions_example() {
  softly.assertThat("George Martin").as("great authors").isEqualTo("JRR Tolkien");  (2)
  softly.assertThat(42).as("response to Everything").isGreaterThan(100); (2)
  softly.assertThat("Gandalf").isEqualTo("Sauron"); (2)
  // No need to call softly.assertAll(), this is automatically done by the JUnitSoftAssertions rule
}

In a similar way you can use JUnitBDDSoftAssertions where assertThat is replaced by then:

@Rule
public final JUnitBDDSoftAssertions softly = new JUnitBDDSoftAssertions();

@Test
void junit4_bdd_soft_assertions_example() {
  softly.then("George Martin").as("great authors").isEqualTo("JRR Tolkien");
  softly.then(42).as("response to Everything").isGreaterThan(100);
  softly.then("Gandalf").isEqualTo("Dauron");
  // No need to call softly.assertAll(), this is automatically done by the JUnitSoftAssertions rule
}
JUnit 5 Soft assertions extension

Since 3.13.0 AssertJ provides SoftAssertionsExtension a JUnit 5 extension that takes care of two things:

  • Injecting a SoftAssertions or BDDSoftAssertions parameter in each test methods

  • Calling assertAll() at the end of each tests

Moreover in 3.16.0 SoftAssertionsExtension is now able to inject any custom soft assertions as long as it implements SoftAssertionsProvider.
See the end of combining soft assertions entry points section for an example.

The term "test method" refers to any method annotated with @Test, @RepeatedTest, @ParameterizedTest, @TestFactory or @TestTemplate. Notably, the extension is compatible with parameterized tests, the parameterized arguments must come first and the soft assertions argument last.

JUnitJupiterSoftAssertions and JUnitJupiterBDDSoftAssertions are now deprecated in favor of SoftAssertionsExtension.

Example:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.api.extension.ExtendWith;
import org.assertj.core.api.BDDSoftAssertions;
import org.assertj.core.api.SoftAssertions;
import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension;

@ExtendWith(SoftAssertionsExtension.class)
public class JUnit5SoftAssertionsExample {

  @Test
  void junit5_soft_assertions_multiple_failures_example(SoftAssertions softly) {
    softly.assertThat("George Martin").as("great authors").isEqualTo("JRR Tolkien");
    softly.assertThat(42).as("response to Everything").isGreaterThan(100);
    softly.assertThat("Gandalf").isEqualTo("Sauron");
    // No need to call softly.assertAll(), this is automatically done by the SoftAssertionsExtension
  }

  @Test
  void junit5_bdd_soft_assertions_multiple_failures_example(BDDSoftAssertions softly) {
    softly.then("George Martin").as("great authors").isEqualTo("JRR Tolkien");
    softly.then(42).as("response to Everything").isGreaterThan(100);
    softly.then("Gandalf").isEqualTo("Sauron");
    // No need to call softly.assertAll(), this is automatically done by the SoftAssertionsExtension
  }

  @ParameterizedTest
  @CsvSource({ "1, 1, 2", "1, 2, 3" })
  // test parameters come first, soft assertion must come last.
  void junit5_soft_assertions_parameterized_test_example(int a, int b, int sum, SoftAssertions softly) {
    softly.assertThat(a + b).as("sum").isEqualTo(sum);
    softly.assertThat(a).isLessThan(sum);
    softly.assertThat(b).isLessThan(sum);
  }

}
Auto Closeable Soft assertions

As AutoCloseableSoftAssertions implements AutoCloseable#close() by calling assertAll(), when used in a try-with-resources block assertAll() is called automatically before exiting the block.

Example:

@Test
void auto_closeable_soft_assertions_example() {
  try (AutoCloseableSoftAssertions softly = new AutoCloseableSoftAssertions()) {
  softly.assertThat("George Martin").as("great authors").isEqualTo("JRR Tolkien");  (2)
  softly.assertThat(42).as("response to Everything").isGreaterThan(100); (2)
  softly.assertThat("Gandalf").isEqualTo("Sauron"); (2)
    // no need to call assertAll, this is done when softly is closed.
  }
}

In a similar way you can use AutoCloseableBDDSoftAssertions where assertThat is replaced by then:

@Test
void auto_closeable_bdd_soft_assertions_example() {
  try (AutoCloseableBDDSoftAssertions softly = new AutoCloseableBDDSoftAssertions()) {
    softly.then("George Martin").as("great authors").isEqualTo("JRR Tolkien");
    softly.then(42).as("response to Everything").isGreaterThan(100);
    softly.then("Gandalf").isEqualTo("Sauron");
    // no need to call assertAll, this is done when softly is closed.
  }
}
Soft assertions with assertSoftly

The assertSoftly static method takes care of calling assertAll() before exiting.

Example:

@Test
void assertSoftly_example() {
  SoftAssertions.assertSoftly(softly -> {
    softly.assertThat("George Martin").as("great authors").isEqualTo("JRR Tolkien");
    softly.assertThat(42).as("response to Everything").isGreaterThan(100);
    softly.assertThat("Gandalf").isEqualTo("Sauron");
    // no need to call assertAll(), assertSoftly does it for us.
  });
}
Combining soft assertions entry points

Since the 3.16.0 version AssertJ provides a way to combine standard soft assertions with custom ones in a single entry point.

Let’s assume we have written an entry point for TolkienCharacter soft assertions so that we can write assertions like:

TolkienSoftAssertions softly = new TolkienSoftAssertions();
softly.assertThat(frodo).hasRace(HOBBIT)
                        .hasName("Frodo");

If we want to check standard soft assertions we could make TolkienSoftAssertions inherit SoftAssertions but if we want to have GoTSoftAssertions too then we are stuck as Java does not allow multiple inheritance.

The 3.16.0 release introduced the SoftAssertionsProvider interface to define soft assertions entry points.

Step 1
The first step consists in extending this interface to expose as many custom entry points as you need.
The typical custom SoftAssertionsProvider interface exposes default assertThat methods, as shown below:

public interface TolkienSoftAssertionsProvider extends SoftAssertionsProvider {
  // custom assertions
  default TolkienCharacterAssert assertThat(TolkienCharacter actual) {
    return proxy(TolkienCharacterAssert.class, TolkienCharacter.class, actual);
  }
}

// let's add a Game of Thrones entry point
public interface GoTSoftAssertionsProvider extends SoftAssertionsProvider {
  // custom assertions
  default GoTCharacterAssert assertThat(GoTCharacter actual) {
    return proxy(GoTCharacterAssert.class, GoTCharacter.class, actual);
  }
}

Step 2
In order to get a concrete entry point exposing all custom entry points, create a class implementing all custom SoftAssertionsProvider`s and extending `AbstractSoftAssertions. AbstractSoftAssertions provides the core internal implementation to collect all errors from the different implemented entry points (it also implements SoftAssertionsProvider).

To get standard soft assertions, inherit from SoftAssertions instead of AbstractSoftAssertions (or BddSoftAssertions to get the BDD flavor).

Let’s define our concrete entry points implementing both TolkienSoftAssertionsProvider and GoTSoftAssertionsProvider:

// we extend SoftAssertions to get standard soft assertions
public class FantasySoftAssertions extends SoftAssertions
                                   implements TolkienSoftAssertionsProvider, GoTSoftAssertionsProvider {

  // we can even add more assertions here
  public HumanAssert assertThat(Human actual) {
    return proxy(HumanAssert.class, Human.class, actual);
  }
}

Step 3
The last step is to use FantasySoftAssertions:

FantasySoftAssertions softly = new FantasySoftAssertions();

// custom TolkienCharacter assertions
softly.assertThat(frodo).hasRace(HOBBIT);

// custom GoTCharacter assertions
softly.assertThat(nedStark).isDead();

// standard assertions
softly.assertThat("Games of Thrones").startsWith("Games")
                                     .endsWith("Thrones");
// verify assertions
softly.assertAll();

Optional step: create a custom JUnit 4 Rule

Because our custom assertions are defined in an interface, we can also combine them with AssertJ’s JUnit 4 rule so that we can use our custom assertions as a test rule for use in JUnit 4:

// we extend JUnitSoftAssertions to get standard soft assertions classes
public class JUnitFantasySoftAssertions extends JUnitSoftAssertions
                                   implements TolkienSoftAssertionsProvider, GoTSoftAssertionsProvider {}

Then in our test class we use it per normal:

public class JUnit4_StandardAndCustomSoftAssertionsExamples {
  @Rule
  public final JUnitFantasySoftAssertions softly = new JUnitFantasySoftAssertions();

  @Test
  public void successful_junit_soft_custom_assertion_example() {
    softly.assertThat(frodo).hasName("Frodo")
                            .hasAge(33);
    softly.assertThat(frodo.age).isEqualTo(33);
  }
}

The rule will automatically take care of calling assertAll() at the end of every test.

Optional step: use SoftAssertionsExtension

JUnit 5 SoftAssertionsExtension calls softly.assertAll() after each test so that we don’t have to do it manually.
Since 3.16.0 it is capable of injecting any SoftAssertionsProvider, we can then inject our custom FantasySoftAssertions:

@ExtendWith(SoftAssertionsExtension.class)
public class JUnit5_StandardAndCustomSoftAssertionsExamples {

  @Test
  public void successful_junit_soft_custom_assertion_example(FantasySoftAssertions softly) {
    softly.assertThat(frodo).hasName("Frodo")
                            .hasAge(33);
    softly.assertThat(frodo.age).isEqualTo(33);
  }
}

2.5.15. Assumptions

Assumptions provide support for conditional test execution, if the assumptions are met the test is executed normally, if they don’t the test is aborted and marked as ignored.

Assumptions are typically used whenever it does not make sense to continue execution of a given test method — a typical usage is running tests depending on a given OS/environment.

All AssertJ assumptions are static methods in the Assumptions class, they match the assertion API but are names assumeThat instead of assertThat. You can also get assumptions through the WithAssumptions interface.

Example resulting in the test to be ignored:

@Test
public void when_an_assumption_is_not_met_the_test_is_ignored() {
  // since this assumption is obviously false ...
  assumeThat(frodo.getRace()).isEqualTo(ORC);
  // ... this assertion is not performed
  assertThat(fellowshipOfTheRing).contains(sauron);
}

Example resulting in the test to be executed normally:

@Test
public void when_all_assumptions_are_met_the_test_is_run_normally() {
  // since this assumption is true ...
  assumeThat(frodo.getRace()).isEqualTo(HOBBIT);
  // ... this assertion is performed
  assertThat(fellowshipOfTheRing).doesNotContain(sauron);
}

2.5.16. Javadoc

http://www.javadoc.io/doc/org.assertj/assertj-core/ is the latest version of assertj core javadoc, each assertion is explained, most of them with code examples so be sure to check it if you want to know what a specific assertion does.

2.6. Extending assertions (TODO)

TODO

2.6.1. Conditions (TODO)

TODO

2.6.2. Custom Assertions (TODO)

TODO

2.7. Migrating assertions

This page will help you converting your existing JUnit assertions to AssertJ ones. Note that both types of assertions can coexist, you don’t have to migrate all at once.

The idea is to convert code like :

assertEquals(expected, actual);

to:

assertThat(actual).isEqualTo(expected);

There are several ways to perform the conversion :

The preferred way is using the scripts as it is quicker and covers more assertions that the other ones.

2.7.1. Assertions migration scripts

It is as simple as running one of the following script depending on which test framework you are using:

Each shell scripts is based on the sed stream editor and regexps. It recursively looks at all *Test.java files and performs search and replace to convert assertions to AssertJ ones.

The script handles the cases where you use an assertion description, for example:

assertEquals("test context", "a", "a");

will be replaced by:

assertThat("a").as("test context").isEqualTo("a");

Note that the script does a best effort and some assertions might not be converted if formatted on multiple lines. Anyway the script usually migrates the vast majority of assertions.

The script works on windows within a bash console like git bash (tested a long time ago) or cygwin (not tested).

Usage

Execute the script in the base directory containing the test files:

cd ./src/test/java
./convert-junit-assertions-to-assertj.sh

If the *Test.java file pattern does not suit you, just specify another as an argument:

# enclose your pattern with double quotes "" to avoid it to be expanded by your shell prematurely
./convert-junit-assertions-to-assertj.sh "*IT.java"

After executing it, you will need to :

  • Optimize imports with your IDE to remove unused imports.

  • If you were using assertEquals with a delta to compare numbers, you will need to statically import org.assertj.core.api.Assertions.within which is how you express deltas in AssertJ (see the number_assertions_with_offset_examples() test at the end of NumberAssertionsExamples).

Script output
Converting JUnit assertions to AssertJ assertions on files matching pattern : *Test.java

 1 - Replacing : assertEquals(0, myList.size()) ............... by : assertThat(myList).isEmpty()
 2 - Replacing : assertEquals(expectedSize, myList.size()) .... by : assertThat(myList).hasSize(expectedSize)
 3 - Replacing : assertEquals(expectedDouble, actual, delta) .. by : assertThat(actual).isCloseTo(expectedDouble, within(delta))
 4 - Replacing : assertEquals(expected, actual) ............... by : assertThat(actual).isEqualTo(expected)
 5 - Replacing : assertArrayEquals(expectedArray, actual) ..... by : assertThat(actual).isEqualTo(expectedArray)
 6 - Replacing : assertNull(actual) ........................... by : assertThat(actual).isNull()
 7 - Replacing : assertNotNull(actual) ........................ by : assertThat(actual).isNotNull()
 8 - Replacing : assertTrue(logicalCondition) ................. by : assertThat(logicalCondition).isTrue()
 9 - Replacing : assertFalse(logicalCondition) ................ by : assertThat(logicalCondition).isFalse()
10 - Replacing : assertSame(expected, actual) ................. by : assertThat(actual).isSameAs(expected)
11 - Replacing : assertNotSame(expected, actual) .............. by : assertThat(actual).isNotSameAs(expected)

Replacing JUnit static imports by AssertJ ones, at this point you will probably need to :
12 --- optimize imports with your IDE to remove unused imports
12 --- add "import static org.assertj.core.api.Assertions.within;" if you were using JUnit number assertions with deltas

2.7.2. Assertions migration regexes

Here’s a list of find/replace expressions to change JUnit assertions into AssertJ assertions (don’t forget to check the regex mode in your editor replace window).

This regexes described in this section are specific to JUnit 4 but you can easily adapt them for JUnit 5 or TestNG.

The order of find/replace is important to benefit from the most relevant AssertJ assertions. For example you should convert assertEquals(0, myList.size()) to assertThat(myList).isEmpty() instead of assertThat(myList.size()).isEqualTo(0).

Converting assertEquals(0, myList.size()) to assertThat(myList).isEmpty()

Find/replace regex:

assertEquals\(0,(.*).size\(\)\); -> assertThat(\1).isEmpty();

It’s better to run this before the assertEqualsisEqualTo conversion to avoid ending with assertThat(myList.size()).isEqualTo(0).

Converting assertEquals(size, myList.size()) to assertThat(myList).hasSize(size)

Find/replace regex:

assertEquals\((.*),(.*).size\(\)\); -> assertThat(\2).hasSize(\1);

It’s better to run this before the assertEqualsisEqualTo conversion to avoid ending with assertThat(myList.size()).isEqualTo(expectedSize).

Converting assertEquals(expected, actual) to assertThat(actual).isEqualTo(expected)

Find/replace regex:

assertEquals\((.*),(.*)\); -> assertThat(\2).isEqualTo(\1);
Converting assertNull(objectUnderTest) to assertThat(objectUnderTest).isNull()

Find/replace regex:

assertNull\((.*)\); -> assertThat(\1).isNull();
Converting assertNotNull(objectUnderTest) to assertThat(objectUnderTest).isNotNull()

Find/replace regex:

assertNotNull\((.*)\); -> assertThat(\1).isNotNull();
Converting assertFalse(logicalCondition) to assertThat(logicalCondition).isFalse()

Find/replace regex:

assertFalse\((.*)\); -> assertThat(\1).isFalse();

2.8. AssertJ Sample Projects

The assertions-examples repository hosts executable AssertJ assertions examples that you can run as JUnit tests. Please have a look at assertions examples sources.

The main branch contains examples with the latest released version of AssertJ modules for Java 8, similarly the java-7 branch contains examples of AssertJ modules for Java 7. You should be able to build those two branches with mvn clean install command.

In your IDE, add src/test/generated-assertions to the project java test sources otherwise you will have errors/missing classes. This is the folder where custom assertions classes are generated by default by the maven assertions generator plugin. Note that Intellij Idea wrongly adds src/test/generated-assertions to the production sources when it should be added the test sources, you will have to fix that in your module/project settings.

Building the with-latest-snapshot-versions branch is a bit more complicated :

  • you need to build the needed SNAPSHOT dependencies before - most probably assertj-core and maybe other modules.

  • run mvn clean install in assertj-examples/assertions-examples.

  • In your IDE, add src/test/generated-assertions to the project java sources if you IDE shows errors/missing classes.

2.9. Release Notes

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

Latest release notes:

Older release notes can be found in the old site: https://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.

2.9.1. 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)

2.9.2. 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]>

2.9.3. 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);

2.9.4. 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")));
}

2.9.5. 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 https://github.com/joel-costigliola/assertj-core/issues/1567 for details.

  • Updates ByteBuddy to version 1.10.0.

  • Fixes some javadoc typos.

  • Enforces banned dependencies with maven-enforcer-plugin.

2.9.6. 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)

2.9.7. 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);

2.9.8. 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).

2.9.9. AssertJ Core 3.12.1

Fix a regression that included a bad module-info.class (thanks Jaro Kuruc) and other minor improvements.

2.9.10. 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 (https://github.com/joel-costigliola/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. TODO document

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

3. AssertJ Guava

3.1. Assertions Guide

This section describes the assertions provided by AssertJ Guava.

3.1.1. ByteSource

Assertion Description

hasSameContentAs

Verifies that the actual ByteSource has the same content as the provided one.

hasSize

Verifies that the size of the actual ByteSource is equal to the given one.

isEmpty

Verifies that the actual ByteSource is empty.

3.1.2. Multimap

Assertion Description

contains

Verifies that the actual Multimap contains the given entries.

containsAllEntriesOf

Verifies that the actual Multimap contains all entries of the given one (it might contain more entries).

containsKeys

Verifies that the actual Multimap contains the given keys.

containsValues

Verifies that the actual Multimap contains the given values for any key.

hasSameEntriesAs

Verifies that the actual Multimap has the same entries as the given one.

hasSize

Verifies that the number of values in the actual Multimap is equal to the given one.

isEmpty

Verifies that the actual Multimap is empty.

isNotEmpty

Verifies that the actual Multimap is not empty.

3.1.3. Multiset

In addition to Iterable assertions, the following are also available.

Assertion Description

contains

Verifies the actual Multiset contains the given value exactly the given number of times.

containsAtLeast

Verifies the actual Multiset contains the given value at least the given number of times.

containsAtMost

Verifies the actual Multiset contains the given value at most the given number of times.

3.1.4. Optional

Assertion Description

contains

Verifies that the actual Optional contains the given value.

extractingCharSequence

Chain assertion on the content of the Optional.

extractingValue

Chain assertion on the content of the Optional.

isAbsent

Verifies that the actual Optional contained instance is absent/null.

isPresent

Verifies that the actual Optional contains a (non-null) instance.

3.1.5. Range

Assertion Description

contains

Verifies that the actual Range contains the given values.

doesNotContain

Verifies that the actual Range does not contain the given values.

hasClosedLowerBound

Verifies that the actual Range lower bound is closed.

hasClosedUpperBound

Verifies that the actual Range upper bound is closed.

hasLowerEndpointEqualTo

Verifies that the actual Range lower endpoint is equal to the given value.

hasOpenedLowerBound

Verifies that the actual Range lower bound is opened.

hasOpenedUpperBound

Verifies that the actual Range upper bound is opened.

hasUpperEndpointEqualTo

Verifies that the actual Range upper endpoint is equal to the given value.

isEmpty

Verifies that the actual Range is empty.

isNotEmpty

Verifies that the actual Range is not empty.

3.1.6. RangeMap

Assertion Description

contains

Verifies that the actual RangeMap contains the given entries.

containsKeys

Verifies that the actual RangeMap contains the given keys.

containsValues

Verifies that the actual RangeMap contains the given values.

isEmpty

Verifies that the actual RangeMap is empty.

isNotEmpty

Verifies that the actual RangeMap is not empty.

3.1.7. RangeSet

Assertion Description

contains

Verifies that the given RangeSet contains the given ranges.

containsAll

Verifies that the given RangeSet contains all the given ranges.

containsAnyOf

Verifies that the given RangeSet contains at least one of the given ranges.

containsAnyRangesOf

Verifies that the given RangeSet contains at least one of the given ranges.

doesNotContain

Verifies that the given RangeSet does not contain any of the given ranges.

doesNotContainAll

Verifies that the given RangeSet does not contain any of the given ranges.

doesNotEnclose

Verifies that the given RangeSet does not enclose the given ranges.

doesNotEncloseAnyRangesOf

Verifies that the given RangeSet does not enclose any range from the given range set.

doesNotEncloseAnyRangesOf

Verifies that the given RangeSet does not enclose any of the given ranges.

doesNotIntersect

Verifies that the given RangeSet does not intersect the given ranges.

doesNotIntersectAnyRangeFrom

Verifies that the given RangeSet does not intersect ranges from the given range set.

doesNotIntersectAnyRangeFrom

Verifies that the given RangeSet does not intersect all the given ranges.

encloses

Verifies that the given RangeSet encloses the given ranges.

enclosesAll

Verifies that the given RangeSet encloses all ranges from the given range set.

enclosesAll

Verifies that the given RangeSet encloses all the given ranges.

enclosesAnyOf

Verifies that the given RangeSet encloses at least one of the given ranges.

enclosesAnyRangesOf

Verifies that the given RangeSet encloses at least one range from the given range set.

enclosesAnyRangesOf

Verifies that the given RangeSet encloses at least one range of the given ranges.

hasSize

Verifies that the given RangeSet has specific size of disconnected Range elements.

intersects

Verifies that the given RangeSet intersects all the given ranges.

intersectsAll

Verifies that the given RangeSet intersects all the given range set.

intersectsAll

Verifies that the given RangeSet intersects all the given ranges.

intersectsAnyOf

Verifies that the given RangeSet intersects at least one of the given ranges.

intersectsAnyRangesOf

Verifies that the given RangeSet intersects at least one range of the given range set.

intersectsAnyRangesOf

Verifies that the given RangeSet intersects at least one of the given ranges.

isEmpty

Verifies that the actual RangeSet is empty.

isNotEmpty

Verifies that the actual RangeSet is not empty.

isNullOrEmpty

Verifies that the actual RangeSet is null or empty.

3.1.8. Table

Assertion Description

containsCell

Verifies that the actual Table contains the mapping of row/column to value.

containsColumns

Verifies that the actual Table contains the given columns.

containsRows

Verifies that the actual Table contains the given rows.

containsValues

Verifies that the actual Table contains the given values for any key.

hasColumnCount

Verifies that the actual Table has the expected number of columns.

hasRowCount

Verifies that the actual Table has the expected number of rows.

hasSize

Verifies that the actual Table has the expected number of cells.

isEmpty

Verifies that the actual Table is empty.

3.2. Release Notes

AssertJ Guava main documentation is still in http://joel-costigliola.github.io/assertj/assertj-guava.html until it is moved to this website.
The new release notes though will be published here.

Latest release notes:

The javadoc for this release can be found here: https://www.javadoc.io/doc/org.assertj/assertj-guava/3.4.0/index.html

3.2.1. AssertJ Guava 3.4.0

Release date : 2020-04-24

Contributors

Thanks to Ilya Koshaleu for its contribution!

New features

Add RangeSet assertions (Ilya Koshaleu):

  • contains​(T…​ ranges): Verifies that the given RangeSet contains the given ranges.

  • containsAll​(Iterable<T> ranges): Verifies that the given RangeSet contains all the given ranges.

  • containsAnyOf​(T…​ ranges): Verifies that the given RangeSet contains at least one of the given ranges.

  • containsAnyRangesOf​(Iterable<T> ranges): Verifies that the given RangeSet contains at least one of the given ranges.

  • doesNotContain​(T…​ ranges): Verifies that the given RangeSet does not contain any of the given ranges.

  • doesNotContainAll​(Iterable<T> ranges): Verifies that the given RangeSet does not contain any of the given ranges.

  • doesNotEnclose​(Range<T>…​ ranges): Verifies that the given RangeSet does not enclose the given ranges.

  • doesNotEncloseAnyRangesOf​(RangeSet<T> rangeSet): Verifies that the given RangeSet does not enclose any range from the given range set.

  • doesNotEncloseAnyRangesOf​(Iterable<Range<T>> ranges): Verifies that the given RangeSet does not enclose any of the given ranges.

  • doesNotIntersect​(Range<T>…​ ranges): Verifies that the given RangeSet does not intersect the given ranges.

  • doesNotIntersectAnyRangeFrom​(RangeSet<T> rangeSet): Verifies that the given RangeSet does not intersect ranges from the given range set.

  • doesNotIntersectAnyRangeFrom​(Iterable<Range<T>> ranges): Verifies that the given RangeSet does not intersect all the given ranges.

  • encloses​(Range<T>…​ ranges): Verifies that the given RangeSet encloses the given ranges.

  • enclosesAll​(RangeSet<T> rangeSet): Verifies that the given RangeSet encloses all ranges from the given range set.

  • enclosesAll​(Iterable<Range<T>> ranges): Verifies that the given RangeSet encloses all the given ranges.

  • enclosesAnyOf​(Range<T>…​ ranges): Verifies that the given RangeSet encloses at least one of the given ranges.

  • enclosesAnyRangesOf​(RangeSet<T> rangeSet): Verifies that the given RangeSet encloses at least one range from the given range set.

  • enclosesAnyRangesOf​(Iterable<Range<T>> ranges): Verifies that the given RangeSet encloses at least one range of the given ranges.

  • hasSize​(int size): Verifies that the given RangeSet has the specific size of disconnected Range elements.

  • intersects​(Range<T>…​ ranges): Verifies that the given RangeSet intersects all the given ranges.

  • intersectsAll​(RangeSet<T> rangeSet): Verifies that the given RangeSet intersects all the given range set.

  • intersectsAll​(Iterable<Range<T>> ranges): Verifies that the given RangeSet intersects all the given ranges.

  • intersectsAnyOf​(Range<T>…​ ranges): Verifies that the given RangeSet intersects at least one of the given ranges.

  • intersectsAnyRangesOf​(RangeSet<T> rangeSet): Verifies that the given RangeSet intersects at least one range of the given range set.

  • intersectsAnyRangesOf​(Iterable<Range<T>> ranges): Verifies that the given RangeSet intersects at least one of the given ranges.

  • isEmpty(): Verifies that the actual RangeSet is empty.

  • isNotEmpty(): Verifies that the actual RangeSet is not empty.

  • isNullOrEmpty(): Verifies that the actual RangeSet is null or empty.

Improvements

  • Javadoc uses AssertJ site beautiful theme :)

  • Uses to assertj-core version 3.15.0.

  • Uses to guava version 29.0-jre.

  • Internal: setup github actions CI build and sonar reporting.

3.2.2. AssertJ Guava 3.3.0

Release date : 2019-11-09

Contributors

Thanks to chrisly42 and Stefano Cordio for their contributions!

New features

Improvements

  • AssertJ’s javadoc are now searchable.

  • Use beautiful AssertJ’s site code style for javadoc :)

  • Migrate to JUnit 5 and assertj-core version 3.14.0.

Fixed

  • Fix for OptionalAssert.contains() that was not working for primitive arrays. (chrisly42)

Deprecated

  • Deprecate org.assertj.guava.data.MapEntry for org.assertj.core.data.MapEntry

Add InstanceOfAssertFactories to allow chain specific type assertions

Add factories for ByteSource, Multimap, Multiset, Optional (guava) and Table to allow to chain specific type assertions from a value initially declared as a less specific type.

Let’s start with the problem asInstanceOf is solving: in the following example we would like to call Table assertions but this is not possible since value is declared as an Object thus only Object assertions are accessible.

// Given a Table declared as an Object
Object actual = HashBasedTable.<Integer, Integer, String> create();

// We would like to call Table assertions but this is not possible since value is declared as an Object
assertThat(actual).isEmpty(); // this does not compile !

Thanks to asInstanceOf we can now tell AssertJ to consider value as a Table in order to call Table assertions.
To do so we need to pass an InstanceOfAssertFactory that can build a TableAssert, fortunately you don’t have to write it, it is already available in InstanceOfAssertFactories!

// Given a Table declared as an Object
Object actual = HashBasedTable.<Integer, Integer, String> create();

// With asInstanceOf, we switch to specific Table assertion by specifying the InstanceOfAssertFactory for Table
assertThat(value).asInstanceOf(InstanceOfAssertFactories.TABLE)
                 .isEmpty();

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.

4. AssertJ Joda

5. AssertJ Neo4j

6. AssertJ DB

assertj db icon

AssertJ-DB provides assertions to test data in a database. It requires Java 8 or later and can be used with either JUnit or TestNG.

If you need additional assertions, please create a ticket in the AssertJ-DB issue tracker.

AssertJ-DB is hosted on github : https://github.com/assertj/assertj-db.

Big thanks to Joel Costigliola for his welcome, for his help and for having accepted to associate this project with a great project like AssertJ.

Régis Pouiller (AssertJ-DB creator)

6.1. Quick start

Suppose that the database contains this table MEMBERS :

ID NAME FIRSTNAME SURNAME BIRTHDATE SIZE

1

'Hewson'

'Paul David'

'Bono'

05-10-60

1.75

2

'Evans'

'David Howell'

'The Edge'

08-08-61

1.77

3

'Clayton'

'Adam'

 

03-13-60

1.78

4

'Mullen'

'Larry'

 

10-31-61

1.70

To quickly start using DataBase assertions, follow the steps below.

6.1.1. Add the assertj-db dependency to your project

Maven
<dependency>
  <groupId>org.assertj</groupId>
  <artifactId>assertj-db</artifactId>
  <version>2.0.0</version>
  <scope>test</scope>
</dependency>
Gradle

For Gradle users (using the Maven Central Repository)

testCompile("org.assertj:assertj-db:2.0.0")
Other dependency management tool

Check this page to find the relevant assertj db dependency declaration.

6.1.2. Statically import org.assertj.db.api.Assertions.assertThat

... and use your preferred IDE code completion after assertThat.

import static org.assertj.db.api.Assertions.assertThat;

import org.assertj.db.type.DateValue;
import org.assertj.db.type.Source;
import org.assertj.db.type.Table;

Table table = new Table(dataSource, "members");

// Check column "name" values
assertThat(table).column("name")
        .value().isEqualTo("Hewson")
        .value().isEqualTo("Evans")
        .value().isEqualTo("Clayton")
        .value().isEqualTo("Mullen");

// Check row at index 1 (the second row) values
assertThat(table).row(1)
        .value().isEqualTo(2)
        .value().isEqualTo("Evans")
        .value().isEqualTo("David Howell")
        .value().isEqualTo("The Edge")
        .value().isEqualTo(DateValue.of(1961, 8, 8))
        .value().isEqualTo(1.77);

In this simple example you can see many `[concepts of AssertJ-DB] (the concepts are simple but I advice you to take the time to get to know them well) :

  • the elements :

    • Table which represents a table in the database

    • Column which represents a column of the table

    • Row which represents a row of the table

    • Value which represents a value in a column or in a row

  • the navigation :

    • The first check, navigates from the table to the column called "name" (column("name") moves the assertion to the column), from this column to the first value (the first call of value() moves to the first value) and after that to each value (each call of value() moves to the next value of the column).

    • The second check, navigates from the table to the row with index 1 (row(1) moves the assertion to the row), from this row to the first value and after that to each value (value() calls have similar behavior for rows and columns).

  • DateValue : Since 2.0.0, AssertJ-DB baseline is Java 8. The preferred way to compare values with date, time and date/time is to use java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime. But for the backward compatibility, it’s always possible to use AssertJ-DB DateValue utilities.

6.2. Concepts

For the examples below, suppose that the database contains these three tables :

Table 1. MEMBERS
ID NAME FIRSTNAME SURNAME BIRTHDATE SIZE

1

'Hewson'

'Paul David'

'Bono'

05-10-60

1.75

2

'Evans'

'David Howell'

'The Edge'

08-08-61

1.77

3

'Clayton'

'Adam'

03-13-60

1.78

4

'Mullen'

'Larry'

10-31-61

1.70

Table 2. ALBUMS
ID RELEASE TITLE NUMBEROFSONGS DURATION LIVE

1

10-20-80

'Boy'

12

42:17

2

10-12-81

'October'

11

41:08

3

02-28-83

'War'

10

42:07

4

11-07-83

'Under a Blood Red Sky'

8

33:25

true

5

10-01-84

'The Unforgettable Fire'

10

42:42

6

06-10-85

'Wide Awake in America'

4

20:30

true

7

03-09-87

'The Joshua Tree'

11

50:11

8

10-10-88

'Rattle and Hum'

17

72:27

9

11-18-91

'Achtung Baby'

12

55:23

10

07-06-93

'Zooropa'

10

51:15

11

03-03-97

'Pop'

12

60:08

12

10-30-00

'All That You Can’t Leave Behind'

11

49:23

13

11-22-04

'How to Dismantle an Atomic Bomb'

11

49:08

14

03-02-09

'No Line on the Horizon'

11

53:44

15

09-09-14

'Songs of Innocence'

11

48:11

Table 3. GROUP
ID NAME

1

'U2'

2

'Coldplay'

6.2.1. Connection to the database

To make assertions on a database, it is necessary to connect. For that, either to use a DataSource or a Source.

DataSource

A DataSource is the classic Java way to get a Connection to a database.

Source

A Source is a way to connect if you have not access to a DataSource or if you do not want to use one. It allows you to pass the necessary connection information as constructor parameters. Below is an example of using a Source to connect to H2 in memory database :

Source source = new Source("jdbc:h2:mem:test", "sa", "");
DataSource with LetterCase

Since 1.1.0

A DataSourceWithLetterCase is a DataSource which allows to indicate the LetterCase for the tables, columns and primary keys.

DataSource ds = new DataSourceWithLetterCase(dataSource,
                                             tableLetterCase,
                                             columnLetterCase,
                                             pkLetterCase);

For more information, see the paragraph on LetterCase.

Source with LetterCase

Since 1.1.0

A SourceWithLetterCase is a Source which allows to indicate the LetterCase for the tables, columns and primary keys.

Source s = new SourceWithLetterCase("jdbc:h2:mem:test", "sa", "",
                                    tableLetterCase,
                                    columnLetterCase,
                                    pkLetterCase);

For more information, see the paragraph on LetterCase.

6.2.2. Elements of the database

Here the elements on which it is possible to make assertions.

Note that, there are only 3 root elements : Table, Request and Changes.

That means that the other elements are components or sub components of a root element.

A root element is an element on which the assertion start (in practice, the parameter of a assertThat(…​) method).

Table

A Table represents a table in the database.

A Table needs a way to connect to the database (either a DataSource or a Source) and a name (the two mandatory constructor parameters).

// Get a DataSource
DataSource dataSource = ...
// Declare the "members" table by using a DataSource
Table table1 = new Table(dataSource, "members");
// Declare the "members" table by using a Source
Table table2 = new Table(source, "members");

The two Tables above (table1 and table2) are equivalent.

Table 4. Representation of "table1" or "table2"
ID NAME FIRSTNAME SURNAME BIRTHDATE SIZE

1

'Hewson'

'Paul David'

'Bono'

05-10-60

1.75

2

'Evans'

'David Howell'

'The Edge'

08-08-61

1.77

3

'Clayton'

'Adam'

03-13-60

1.78

4

'Mullen'

'Larry'

10-31-61

1.70

For a Table, it is possible to choose the columns to include and to exclude in the assertions.

// Get the data of the "id" and "name" columns of the "members" table
Table table3 = new Table(source, "members", new String[] { "id", "name" }, null);
// Get the data of the "members" table but not of the "birthdate" column
Table table4 = new Table(source, "members", null, new String[] { "birthdate" });
// Get the data of the "name" column of the "members" table (because "id" is included and excluded)
Table table5 = new Table(source, "members", new String[] { "id", "name" }, new String[] { "id" });
Table 5. Representation of "table3"
ID NAME

1

'Hewson'

2

'Evans'

3

'Clayton'

4

'Mullen'

Table 6. Representation of "table4"
ID NAME FIRSTNAME SURNAME SIZE

1

'Hewson'

'Paul David'

'Bono'

1.75

2

'Evans'

'David Howell'

'The Edge'

1.77

3

'Clayton'

'Adam'

1.78

4

'Mullen'

'Larry'

1.70

Table 7. Representation of "table5"
NAME

'Hewson'

'Evans'

'Clayton'

'Mullen'

Since version 1.2.0, there are the possibility to indicate delimiters (start delimiter and end delimiter) and Order.

The delimiters are usefull when the table name or column name is a reserved word or contains special characters (like space or '%'). Order allows to choose the order of the Row.

// The line code below throws SQLException because "group" is SQL reserved word
Table table6 = new Table(source, "group");
// Get the data of the "group" table by using "`" delimiter
// That generates a request
Table table7 = new Table(source, "group", '`', '`');

// Get the data from "members" table and order on "name" column in ascending order
Table table8 = new Table(source, "members", new Order[] {
                                                        Order.asc("name")
                                                      });
Table 8. Representation of "table7"
ID NAME

1

'U2'

2

'Colplay'

Table 9. Representation of "table8"
ID NAME FIRSTNAME SURNAME BIRTHDATE SIZE

3

'Clayton'

'Adam'

03-13-60

1.78

2

'Evans'

'David Howell'

'The Edge'

08-08-61

1.77

1

'Hewson'

'Paul David'

'Bono'

05-10-60

1.75

4

'Mullen'

'Larry'

10-31-61

1.70

Request

A Request represents a SQL request on the database.

Like a Table, a Request needs a way to connect to the database (either a DataSource or a Source).

// Get a DataSource
DataSource dataSource = ...
// Declare a request which gets the name and the firstname of the corresponding members
// by using a Source
Request request1 = new Request(source,
                               "select name, firstname from members where id = 2 or id = 3");
// Declare a request which gets the name and the firstname of the corresponding members
// by using the DataSource
Request request2 = new Request(dataSource,
                               "select name, firstname from members where id = 2 or id = 3");

The two Requests above (request1 and request2) are equivalent.

Table 10. Representation of "request1" or "request2"
NAME FIRSTNAME SURNAME

'Evans'

'David Howell'

'The Edge'

'Clayton'

'Adam'

For a Request, it is possible to use a simple SQL request or a SQL request with one or many parameters.

// Declare a request which gets the name and the firstname of the members
// and use "%e%" as a parameter
Request request3 = new Request(dataSource,
                               "select name, firstname from members " +
                               "where name like ?;",
                               "%e%");
// Declare a request which gets the name and the firstname of the members
// and use "%e%" and "%Paul%" as parameters
Request request4 = new Request(dataSource,
                               "select name, firstname from members " +
                               "where name like ? and firstname like ?;",
                               "%e%",
                               "%Paul%");
Table 11. Representation of "request3"
NAME FIRSTNAME SURNAME

'Hewson'

'Paul David'

'Bono'

'Evans'

'David Howell'

'The Edge'

'Mullen'

'Larry'

Table 12. Representation of "request4"
NAME FIRSTNAME SURNAME

'Hewson'

'Paul David'

'Bono'

Changes

The Changes are the differences of states in database between a start point and a end point.

db changes concept

Assume that there are these SQL statements between the start point and the end point.

DELETE FROM ALBUMS WHERE ID = 15;
INSERT INTO MEMBERS(ID, NAME, FIRSTNAME) VALUES(5, 'McGuiness', 'Paul');
UPDATE MEMBERS SET SURNAME = 'Bono Vox' WHERE ID = 1;
UPDATE ALBUMS SET NAME = 'Rattle & Hum', LIVE = true WHERE ID = 8;
// Get a DataSource
DataSource dataSource = ...
// The changes can be on a DataSource or on a Source
Changes changes1 = new Changes(dataSource);
Changes changes2 = new Changes(source);
// The changes can also be on a Table or on a Request
Changes changes3 = new Changes(table4);
Changes changes4 = new Changes(request3);
Changes changes5 = new Changes(request4);
// The names of the columns used for the primary key are found in the metadata for a table
// but for a request it can be important to set the primary key
Changes changes6 = new Changes(request4).setPksName("name");

The two Changes above (changes1 and changes2) are equivalent.

The two Changes above (changes4 and changes5) are also equivalent.

The changes are ordered :

  • First by the type of the change : creation, modification and after deletion

  • After if it a change on a table by the name of the table

  • To finish by the values of the primary key and if there are no primary key by the values of the row (for a modification)

As indicated above, the primary key is used to order the changes. But more important, the primary key is used to determinate which rows at the same with modifications.

In Representation of "changes4" or "changes5" the modification of first row of the table become a creation and deletion.

Table 13. Representation of "changes1" or "changes2"

Creation

"MEMBERS" table

5 as PK

ID NAME FIRSTNAME SURNAME BIRTHDATE SIZE

At start point

At end point

5

'McGuiness'

'Paul'

Modification

"ALBUMS" table

8 as PK

ID RELEASE TITLE NUMBEROFSONGS DURATION LIVE

At start point

8

10-10-88

'Rattle and Hum'

17

72:27

At end point

8

10-10-88

'Rattle & Hum'

17

72:27

true

Modification

"MEMBERS" table

1 as PK

ID NAME FIRSTNAME SURNAME BIRTHDATE SIZE

At start point

1

'Hewson'

'Paul David'

'Bono'

05-10-60

1.75

At end point

1

'Hewson'

'Paul David'

'Bono Vox'

05-10-60

1.75

Deletion

"ALBUMS" table

15 as PK

ID RELEASE TITLE NUMBEROFSONGS DURATION LIVE

At start point

15

09-09-14

'Songs of Innocence'

11

48:11

At end point

Table 14. Representation of "changes3"

Creation

"MEMBERS" table

5 as PK

ID NAME FIRSTNAME SURNAME SIZE

At start point

At end point

5

'McGuiness'

'Paul'

Modification

"MEMBERS" table

1 as PK

ID NAME FIRSTNAME SURNAME SIZE

At start point

1

'Hewson'

'Paul David'

'Bono'

1.75

At end point

1

'Hewson'

'Paul David'

'Bono Vox'

1.75

Table 15. Representation of "changes4" or "changes5"

Creation

No PK

NAME FIRSTNAME SURNAME

At start point

At end point

'Hewson'

'Paul David'

'Bono Vox'

Creation

No PK

NAME FIRSTNAME SURNAME

At start point

At end point

'McGuiness'

'Paul'

Deletion

No PK

NAME FIRSTNAME SURNAME

At start point

At end point

'Hewson'

'Paul David'

'Bono Vox'

Table 16. Representation of "changes6"

Creation

'McGuiness' as PK

NAME FIRSTNAME SURNAME

At start point

At end point

'McGuiness'

'Paul'

Modification

'Hewson' as PK

NAME FIRSTNAME SURNAME

At start point

'Hewson'

'Paul David'

'Bono'

At end point

'Hewson'

'Paul David'

'Bono Vox'

Change

A Change is an element of the Changes.

Below framed in red the first Change of "changes3".

Table 17. Representation of "changes3"

Creation

"MEMBERS" table

5 as PK

ID NAME FIRSTNAME SURNAME SIZE

At start point

At end point

5

'McGuiness'

'Paul'

Modification

"MEMBERS" table

1 as PK

ID NAME FIRSTNAME SURNAME SIZE

At start point

1

'Hewson'

'Paul David'

'Bono'

1.75

At end point

1

'Hewson'

'Paul David'

'Bono Vox'

1.75

Row

A Row can represent a row of a Table , of a Request or of a Change.

Below framed in red the third Row of "table4".

Table 18. Representation of "table4"
ID NAME FIRSTNAME SURNAME SIZE

1

'Hewson'

'Paul David'

'Bono'

1.75

2

'Evans'

'David Howell'

'The Edge'

1.77

3

'Clayton'

'Adam'

1.78

4

'Mullen'

'Larry'

1.70

Below framed in red the second Row of "request3".

Table 19. Representation of "request3"
NAME FIRSTNAME SURNAME

'Hewson'

'Paul David'

'Bono'

'Evans'

'David Howell'

'The Edge'

'Mullen'

'Larry'

Below framed in red the Row at end point of the second Change of "changes3".

Table 20. Representation of "changes3"

Creation

"MEMBERS" table

5 as PK

ID NAME FIRSTNAME SURNAME SIZE

At start point

At end point

5

'McGuiness'

'Paul'

Modification

"MEMBERS" table

1 as PK

ID NAME FIRSTNAME SURNAME SIZE

At start point

1

'Hewson'

'Paul David'

'Bono'

1.75

At end point

1

'Hewson'

'Paul David'

'Bono Vox'

1.75

Column

A Column can represent a column of a Table , of a Request or of a Change.

Below framed in red the second Column of "table4".

Table 21. Representation of "table4"
ID NAME FIRSTNAME SURNAME SIZE

1

'Hewson'

'Paul David'

'Bono'

1.75

2

'Evans'

'David Howell'

'The Edge'

1.77

3

'Clayton'

'Adam'

1.78

4

'Mullen'

'Larry'

1.70

Below framed in red the second Column of "request3".

Table 22. Representation of "request3"
NAME FIRSTNAME SURNAME

'Hewson'

'Paul David'

'Bono'

'Evans'

'David Howell'

'The Edge'

'Mullen'

'Larry'

Below framed in red the fourth Column of the second Change of "changes3".

Table 23. Representation of "changes3"

Creation

"MEMBERS" table

5 as PK

ID NAME FIRSTNAME SURNAME SIZE

At start point

At end point

5

'McGuiness'

'Paul'

Modification

"MEMBERS" table

1 as PK

ID NAME FIRSTNAME SURNAME SIZE

At start point

1

'Hewson'

'Paul David'

'Bono'

1.75

At end point

1

'Hewson'

'Paul David'

'Bono Vox'

1.75

Value

A value can be in a Row or in a Column.

Below framed in red (depending of the path) :

  • the second value of the third Row of "table4"

  • the third value of the second Column of "table4"

Table 24. Representation of "table4"
ID NAME FIRSTNAME SURNAME SIZE

1

'Hewson'

'Paul David'

'Bono'

1.75

2

'Evans'

'David Howell'

'The Edge'

1.77

3

'Clayton'

'Adam'

1.78

4

'Mullen'

'Larry'

1.70

Below framed in red (depending of the path) :

  • the second value of the second Row of "request3"

  • the second value of the second Column of "request3"

Table 25. Representation of "request3"
NAME FIRSTNAME SURNAME

'Hewson'

'Paul David'

'Bono'

'Evans'

'David Howell'

'The Edge'

'Mullen'

'Larry'

Below framed in red (depending of the path) :

  • the fourth value of the Row at end point of the second Change of "changes3"

  • the value at end point of the fourth Column of the second Change of "changes3"

Table 26. Representation of "changes3"

Creation

"MEMBERS" table

5 as PK

ID NAME FIRSTNAME SURNAME SIZE

At start point

At end point

5

'McGuiness'

'Paul'

Modification

"MEMBERS" table

1 as PK

ID NAME FIRSTNAME SURNAME SIZE

At start point

1

'Hewson'

'Paul David'

'Bono'

1.75

At end point

1

'Hewson'

'Paul David'

'Bono Vox'

1.75

6.2.3. Type

Data Type

As see above there are three root elements of database, but only Table and Request are data elements. All the possible types of data are contained in the DataType enumeration.

The type of the data can be :

Change Type

The change can be a creation, a modification or a deletion. All the possible types of change are contained in the ChangeType enumeration.

The type of the change depends of operation on database :

Value Type

The value can be a date, a boolean or a text for example. All the possible types of value are contained in the ValueType enumeration.

The type of the value depends of class of the object given by java.sql when the data are got from database :

  • BYTES for a array of bytes (byte[])

  • BOOLEAN for a java.lang.Boolean

  • TEXT for a java.lang.String

  • DATE for a java.sql.Date

  • TIME for a java.sql.Time

  • DATE_TIME for a java.sql.Timestamp

  • UUID for a java.util.UUID (since 1.1.0)

  • NUMBER for a java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Double, java.lang.Float or java.math.BigDecimal

  • NOT_IDENTIFIED for other cases (for example when the value is null)

Order Type

Since 1.2.0

The order can be a ascending or descending. All the possible types of order are contained in the Table.Order.OrderType enumeration.

The type of the order can be :

  • ASC for an ascending order

  • DESC for a descending order

6.2.4. Navigation

The navigation offers the ability to chain assertions at different levels and instructions to go inside the sub-elements and return to root element.

There are examples of the navigation in NavigationExamples.java

With a Table or a Request as root

The assertThat(…​) static method of org.assertj.db.api.Assertions allows to create a root assertion on a Table.

import static org.assertj.db.api.Assertions.assertThat;

assertThat(table)...

or on a Request.

import static org.assertj.db.api.Assertions.assertThat;

assertThat(request)...

From these root assertions, it is possible to navigate to the sub elements and return to the root element as in the picture below.

db navigation with table or request

More details on this concept in assertj-db-features-highlight.html#tableorrequestasroot[feature highlight].

With Changes as root

The assertThat(…​) static method of org.assertj.db.api.Assertions allows to create a root assertion on Changes.

import static org.assertj.db.api.Assertions.assertThat;

assertThat(changes)...

From this root assertion, it is possible to navigate to the sub elements and return to the root element as in the picture below.

db navigation with changes

More details on this concept in feature highlight.

6.2.5. DateValue, TimeValue and DateTimeValue

Since 2.0.0, AssertJ-DB baseline is Java 8. The preferred way to compare values with date, time and date/time is to use java.time.LocalDate, java.time.LocalTime, java.time.LocalDateTime directly.

But for the backward compatibility, it’s always possible to use AssertJ-DB DateValue utilities.

So The DateValue, TimeValue and DateTimeValue classes are simpler but contains this kind of informations.

There is 4 kinds of static methods to instantiate these values :

  • of which receives the informations as int parameters

DateValue dateValue = DateValue.of(2007, 12, 23);

// With hours and minutes only
TimeValue timeValue1 = TimeValue.of(9, 1);
// With seconds additional
TimeValue timeValue2 = TimeValue.of(9, 1, 6);
// With nanoseconds additional
TimeValue timeValue3 = TimeValue.of(9, 1, 6, 3);

// With date only (so hour is midnight)
DateTimeValue dateTimeValue1 = DateTimeValue.of(dateValue);
// With date and time
DateTimeValue dateTimeValue2 = DateTimeValue.of(dateValue, timeValue1);
  • from which receives the equivalent from java.sql package (java.sql.Date, java.sql.Time and java.sql.Timestamp) or a java.util.Calendar (since 1.1.0)

Date date = Date.valueOf("2007-12-23");
DateValue dateValue = DateValue.from(date);

Time time = Time.valueOf("09:01:06");
TimeValue timeValue = TimeValue.from(time);

Timestamp timestamp = Timestamp.valueOf("2007-12-23 09:01:06.000000003");
DateTimeValue dateTimeValue = DateTimeValue.from(timestamp);

// Since 1.1.0
Calendar calendar = Calendar.getInstance();
DateValue dateValueFromCal = DateValue.from(calendar);
TimeValue timeValueFromCal = TimeValue.from(calendar);
DateTimeValue dateTimeValueFromCal = DateTimeValue.from(calendar);
  • parse which receives a String to represent the value (this method can throw a ParseException)

DateValue dateValue = DateValue.parse("2007-12-23");

// With hours and minutes only
TimeValue timeValue1 = TimeValue.parse("09:01");
// With seconds additional
TimeValue timeValue2 = TimeValue.parse("09:01:06");
// With nanoseconds additional
TimeValue timeValue3 = TimeValue.parse("09:01:06.000000003");

// With date only (so hour is midnight)
DateTimeValue dateTimeValue1 = DateTimeValue.parse("2007-12-23");
// With date and time (hours and minutes only)
DateTimeValue dateTimeValue2 = DateTimeValue.parse("2007-12-23T09:01");
// With date and time (seconds additional)
DateTimeValue dateTimeValue2 = DateTimeValue.parse("2007-12-23T09:01:06");
// With date and time (nanoseconds additional)
DateTimeValue dateTimeValue2 = DateTimeValue.parse("2007-12-23T09:01:06.000000003");
  • now (since 1.1.0) which create an instance corresponding to the current moment.

DateValue dateValue = DateValue.now();                   // The current date
TimeValue timeValue = TimeValue.now();                   // The current time
DateTimeValue dateTimeValue = DateTimeValue.now();       // The current date/time

All these static methods (except for now method) have equivalent constructors.

6.2.6. Default description

In assertj, it is possible to add a description with the methods of the Descriptable interface. This description is used in the error message if the assertion fails.

Due to the navigation, it is more complicated in asserj-db to know on which element an error is thrown. So to help the tester, there are default descriptions.

For example :

  • "members table" for an assertion on a table

  • "'select * from actor' request" for an assertion on a request

  • "'select id, name, firstname, bi…​' request" for an assertion on a request with more text

  • "Row at index 0 of members table" for an assertion on a row of a table

  • "Column at index 0 (column name : ID) of 'select * from members' request" for an assertion on a column of a request

  • "Value at index 0 of Column at index 0 (column name : ID) of 'select * from members' request" for an assertion on a value of a column of a request

  • "Value at index 0 (column name : ID) of Row at index 0 of 'select * from members' request" for an assertion on a value of a row of a request

  • "Value at index 0 (column name : ID) of Row at end point of Change at index 0 (on table : MEMBERS and with primary key : [4]) of Changes on tables of 'sa/jdbc:h2:mem:test' source" for an assertion on a value of the row at end point of a change on a table

This default description can be replaced by the choice of the tester by using the methods of Descriptable.

6.2.7. Letter Case of the database

Since 1.1.0

Databases have different letter cases for the name of the elements. For example, the name of the table can be upper case either the name is inputed in upper case or not. So this concept (and feature too) is here to manage these shades.

It is possible to declare a LetterCase with a DataSource with LetterCase or with a Source with LetterCase.

The concept of LetterCase is composed of CaseConversion and CaseComparison.

CaseConversion

The CaseConversion is used when getting a name with letter case from database : a table name, a column name or a primary key name.

There are three conversions modes : UPPER which converts to upper case ("Name" becomes "NAME"), LOWER which converts to lower case ("Name" becomes "name") and NO which keeps the case ("Name" remains "Name").

Each name (table, column and primary key) got from the database is converted using a CaseConversion.

CaseComparison

The CaseComparison is used when comparing something with letter case from database or with a parameter.

There are two comparison modes : IGNORE which compares String`s by ignoring the case (`"Name" is considered equal to "NAME") and STRICT which compares String`s strictly (`"Name" is considered different from "NAME").

During navigation (e.g. from table to column) and assertion (e.g. on column name), the name are compared using a CaseComparison.

LetterCase

A LetterCase is created with the getLetterCase static method which has a CaseConversion and a CaseComparison as parameters.

LetterCase letterCase = LetterCase.getLetterCase(CaseConversions.NO, CaseComparisons.IGNORE)

In AssertJ-DB, there are three different uses of a LetterCase : the table name, the column name and the primary key name. That is the reason why the DataSourceWithLetterCase and the SourceWithLetterCase constructors have three LetterCase parameters.

The LetterCase on the tables is used :

  • to convert the table name : when a name is got from the database like for the Table instantiation or for the table with changes found with Changes.

  • to compare the table name : for the instantiation when the table is search in the database for Table, for navigation (e.g. from changes to a change on a table) or for a assertion (like isOnTable(String name)).

The LetterCase on the columns is used :

  • to convert the column name : when a column name is got from the database for a table or a request

  • to compare the column name : for the navigation (e.g. from a table to a column) or for a assertion (like hasColumnName(String columnName)).

The LetterCase on the primary keys is used :

  • to convert the primary key name : when a primary key name is got from the database for a table

  • to compare the primary key name : for a assertion (like hasPksNames(String…​ names)).

The different LetterCase are explictly indicated for DataSourceWithLetterCase and SourceWithLetterCase. But for DataSource and Source, there are LetterCase too but there are implicit :

  • NO conversion and IGNORE comparison for table names

  • UPPER conversion and IGNORE comparison for the column and primary key name

In this example, The uses of Source and SourceWithLetterCase are equivalent :

Source source = new Source("jdbc:h2:mem:test", "sa", "");
Table table = new Table(source, "members");

LetterCase tableLetterCase = LetterCase.getLetterCase(CaseConversions.NO, CaseComparisons.IGNORE);
LetterCase columnLetterCase = LetterCase.getLetterCase(CaseConversions.UPPER, CaseComparisons.IGNORE);
LetterCase pkLetterCase = LetterCase.getLetterCase(CaseConversions.UPPER, CaseComparisons.IGNORE);
Source sourceWithLC = new SourceWithLetterCase("jdbc:h2:mem:test", "sa", "",
                                               tableLetterCase,
                                               columnLetterCase,
                                               pkLetterCase);
Table tableWithLC = new Table(sourceWithLC, "members");

And in this example, the uses of DataSource and DataSourceWithLetterCase are equivalent :

DataSource dataSource = .....
Table table = new Table(dataSource, "members");

LetterCase tableLetterCase = LetterCase.getLetterCase(CaseConversions.NO, CaseComparisons.IGNORE);
LetterCase columnLetterCase = LetterCase.getLetterCase(CaseConversions.UPPER, CaseComparisons.IGNORE);
LetterCase pkLetterCase = LetterCase.getLetterCase(CaseConversions.UPPER, CaseComparisons.IGNORE);
DataSource dataSourceWithLC = new DataSourceWithLetterCase(dataSource,
                                                           tableLetterCase,
                                                           columnLetterCase,
                                                           pkLetterCase);
Table tableWithLC = new Table(dataSourceWithLC, "members");

Note that the letter case is extensible because the getLetterCase static method’s parameters are instances of the CaseConversion and the CaseComparison interfaces. So this is not limited to the implementations in the corresponding enumerations.

6.2.8. Output

Since 1.1.0

It can be interesting to view the values on which an assertion is made (for example for debugging). The output allows that.

This is a simple example :

import static org.assertj.db.output.Outputs.output;

Table table = new Table(dataSource, "members");

// Output the content of the table in the console
output(table).toConsole();

These lines give the result below :

[MEMBERS table]
|-----------|---------|-----------|-----------|--------------|-----------|-----------|-----------|
|           |         | *         |           |              |           |           |           |
|           | PRIMARY | ID        | NAME      | FIRSTNAME    | SURNAME   | BIRTHDATE | SIZE      |
|           | KEY     | (NUMBER)  | (TEXT)    | (TEXT)       | (TEXT)    | (DATE)    | (NUMBER)  |
|           |         | Index : 0 | Index : 1 | Index : 2    | Index : 3 | Index : 4 | Index : 5 |
|-----------|---------|-----------|-----------|--------------|-----------|-----------|-----------|
| Index : 0 | 1       | 1         | Hewson    | Paul David   | Bono      | 05-10-60  | 1.75      |
| Index : 1 | 2       | 2         | Evans     | David Howell | The Edge  | 08-08-61  | 1.77      |
| Index : 2 | 3       | 3         | Clayton   | Adam         |           | 03-13-60  | 1.78      |
| Index : 4 | 4       | 4         | Mullen    | Larry        |           | 10-31-61  | 1.70      |
|-----------|---------|-----------|-----------|--------------|-----------|-----------|-----------|

In the example above, the output is in plain text in the console. It is possible to change the type of the output and the destination.

Type of output

There are two outputs already implemented :

  • PLAIN : shown in the example above (the default output type)

  • HTML : which represents the result as an HTML document

// Change the output of the table to be HTML
output(table).withType(OutputType.HTML).....;

Note that the type of output is extensible because the withType(Output outputType) method’s parameter is an instance of the Output interface. So this is not limited to the implementations in the OutputType enum.

Destination

The destination is the way to print the display. There are three destinations :

Note that with this last method the possibilities of destination are really flexible.

These three methods are fluent. In this short example, the output is a plain text representation in the console and a html output in a file :

// Display the content of the table with plain text in the console
// and with HTML output in the file
output(table).toConsole().withType(OutputType.HTML).toFile("test.html");

6.3. Features highlight

Before reading this page, it is recommended to be familiar with the concepts of AssertJ-DB.

The purpose of this page is to show the different features of AssertJ-DB.

6.3.1. Navigation

With a Table or a Request as root

As shown in the concepts (to easily understand this chapter it is important to know the concepts of assertj-db), the assertThat(…​) static method is used to begin an assertion on a Table or on a Request.

The navigation from a table or from a request are similar, so in most of the examples below a table will be used :

assertThat(tableOrRequest)...

If there is a difference if will be specified.

All the navigation methods work from an origin point. That means that if the method is executed from another point, it is like the execution is from the point of view of the origin.

There are some recurring points in the different navigation methods :

  • a method without parameter which allows to navigate on the next element after the element reached on the last call (if it is the first call, navigate to the first element)

  • a method with an int parameter (an index) which allows to navigate on the element which is at the corresponding index

  • a method with an String parameter (a column name) which allows to navigate on the element corresponding at the column name

To a Row

These methods are described in the ToRow interface.

The row() method allows to navigate to the next row after the row reached on the last call.

// If it is the first call, navigate to the first row
assertThat(tableOrRequest).row()...
// It is possible to chain the calls to navigate to the next row
// after the first row (so the second row)
assertThat(tableOrRequest).row().row()...

The row(int index) method with index as parameter allows to navigate to the row corresponding to row at the index.

// Navigate to the row at index 2
assertThat(tableOrRequest).row(2)...
// It is possible to chain the calls to navigate to another row.
// Here row at index 6
assertThat(tableOrRequest).row(2).row(6)...
// It is possible to combine the calls to navigate to the next row
// after the row at index 2. Here row at index 3
assertThat(tableOrRequest).row(2).row()...

This picture shows from where it is possible to navigate to a row.

db navigation with table or request to row

The origin point of the row(…​) methods is the Table or the Request. So if the method is executed from a row, from a column or from a value it is like if the method was executed from the Table or the Request.

When the position is on a row, it is possible to return to the origin.

// Return to the table from a row of a table
assertThat(table).row().returnToTable()...
// Return to the request from a row of a request
assertThat(request).row().returnToRequest()...

That also means that the two navigations below are equivalent.

// Navigate to the first row
// Return to the table from this row
// Navigate to the next row
assertThat(table).row().returnToTable().row()...
// The same thing is done but the return to the table is implicit
assertThat(table).row().row()...
To a Column

These methods are described in the ToColumn interface.

The column() method allows to navigate to the next column after the column reached on the last call.

// If it is the first call, navigate to the first column
assertThat(tableOrRequest).column()...
// It is possible to chain the calls to navigate to the next column
// after the first column (so the second column)
assertThat(tableOrRequest).column().column()...

The column(int index) method with index as parameter allows to navigate to the column corresponding to column at the index.

// Navigate to the column at index 2
assertThat(tableOrRequest).column(2)...
// It is possible to chain the calls to navigate to another column.
// Here column at index 6
assertThat(tableOrRequest).column(2).column(6)...
// It is possible to combine the calls to navigate to the next column
// after the column at index 2. Here column at index 3
assertThat(tableOrRequest).column(2).column()...
// It is possible to combine the calls with other navigation methods
// Here first column
assertThat(tableOrRequest).row(2).column()...
// Here column at index 3
assertThat(tableOrRequest).row(2).column(3)...
// Here column at index 4 because the origin remember last navigation to a column
assertThat(tableOrRequest).column(3).row(2).column()...

The column(String columnName) method with columnName as parameter allows to navigate to the column corresponding to the column with the column name.

// Navigate to the column with the name "SURNAME"
assertThat(tableOrRequest).column("surname")...
// Like for the other methods, it is possible to chain the calls
assertThat(tableOrRequest).column("surname").column().column(6).column("id")...

This picture shows from where it is possible to navigate to a column.

db navigation with table or request to column

The origin point of the column(…​) methods is the Table or the Request. So if the method is executed from a row, from a column or from a value it is like if the method was executed from the Table or The Request.

When the position is on a column, it is possible to return to the origin.

// Return to the table from a column of a table
assertThat(table).column().returnToTable()...
// Return to the request from a column of a request
assertThat(request).column().returnToRequest()...

That also means that the two navigations below are equivalent.

// Navigate to the first column
// Return to the table from this column
// Navigate to the next column
assertThat(table).column().returnToTable().column()...
// The same thing is done but the return to the table is implicit
assertThat(table).column().column()...
To a Value

These methods are described in the ToValue and the ToValueFromRow interfaces.

The value() method allows to navigate to the next value after the value reached on the last call.

// If it is the first call, navigate to the first value
assertThat(tableOrRequest).row().value()...
// It is possible to chain the calls to navigate to the next value
// after the first value (so the second value)
assertThat(tableOrRequest).column().value().value()...

The value(int index) method with index as parameter allows to navigate to the value corresponding to value at the index.

// Navigate to the value at index 2
assertThat(tableOrRequest).column().value(2)...
// It is possible to chain the calls to navigate to another value.
// Here value at index 6
assertThat(tableOrRequest).row(4).value(2).value(6)...
// It is possible to combine the calls to navigate to the next value
// after the value at index 2. Here value at index 3
assertThat(tableOrRequest).column(4).value(2).value()...
// Here value at index 4 because the origin remember last navigation to a column
assertThat(tableOrRequest).column().value(3).row(2).column(0).value()...

The value(String columnName) method with columnName as parameter (only available from a row) allows to navigate to the value of the column corresponding to the column with the column name.

// Navigate to the value of the column with the name "SURNAME"
assertThat(tableOrRequest).row().value("surname")...
// Like for the other methods, it is possible to chain the calls
assertThat(tableOrRequest).row().value("surname").value().value(6).value("id")...

This picture shows from where it is possible to navigate to a value.

db navigation with table or request to value

The origin point of the value(…​) methods is the Row or the Column. So if the method is executed from a value it is like if the method was executed from the Row or The Column.

When the position is on a value, it is possible to return to the origin.

// Return to the column from a value
assertThat(table).column().value().returnToColumn()...
// Return to the row from a value
assertThat(request).row().value().returnToRow()...

That also means that the two navigations below are equivalent.

// Navigate to the first column
// Navigate to the first value
// Return to the column from this value
// Navigate to the next value
assertThat(table).column().value().returnToColumn().value()...
// The same thing is done but the return to the column is implicit
assertThat(table).column().value().value()...
With Changes as root
To Changes

These methods are described in the ToChanges interface.

The ofCreation() method allows to navigate to the changes of creation.

// Navigate to the changes of creation
assertThat(changes).ofCreation()...

The ofCreationOnTable() method with tableName as parameter allows to navigate to the changes of creation of a table.

// Navigate to the changes of creation on the "members" table
assertThat(changes).ofCreationOnTable("members")...

The ofCreation() method allows to navigate to the changes of modification.

// Navigate to the changes of modification
assertThat(changes).ofModification()...

The ofModificationOnTable() method with tableName as parameter allows to navigate to the changes of modification of a table.

// Navigate to the changes of modification on the "members" table
assertThat(changes).ofModificationOnTable("members")...

The ofCreation() method allows to navigate to the changes of deletion.

// Navigate to the changes of deletion
assertThat(changes).ofDeletion()...

The ofDeletionOnTable() method with tableName as parameter allows to navigate to the changes of deletion of a table.

// Navigate to the changes of deletion on the "members" table
assertThat(changes).ofDeletionOnTable("members")...

The onTable(String tableName) method with tableName as parameter allows to navigate to the changes of a table.

// Navigate to all the changes on the "members" table
assertThat(changes).onTable("members")...

The ofAll() method allows to navigate to all the changes.

// Navigate to all the changes
assertThat(changes).ofAll()...
// The navigation can be chained
assertThat(changes).ofCreation().ofAll()...

This picture shows from where it is possible to navigate to changes.

db navigation with changes to changes

The origin point of these methods is the Changes. So if the method is executed from a change, a column, a row or a value it is like if the method was executed from the Changes.

To a Change

These methods are described in the ToChange interface.

The change() method allows to navigate to the next change after the change reached on the last call.

// If it is the first call, navigate to the first change
assertThat(changes).change()...
// It is possible to chain the calls to navigate to the next change
// after the first change (so the second change)
assertThat(changes).change().change()...

The change(int index) method with index as parameter allows to navigate to the change corresponding to change at the index.

// Navigate to the change at index 2
assertThat(changes).change().change(2)...
// It is possible to chain the calls to navigate to another change.
// Here change at index 7
assertThat(changes).change(6).change()...

The changeOnTable(String tableName) method with tableName as parameter allows to navigate to the next change corresponding to the table name after the change corresponding to the table name reached on the last call.

// If it is the first call, navigate to the first change on "members" table
assertThat(changes).changeOnTable("members")...
// It is possible to chain the calls to navigate to the next change on the "members" table
// after the first change on the "members" table (so the second change)
assertThat(changes).changeOnTable("members").changeOnTable("members")...

The changeOnTable(String tableName, int index) method with tableName and index as parameters allows to navigate to the change corresponding to change on the table name at the index.

// Navigate to the change at index 2 of "members" table
assertThat(changes).changeOnTable("members").changeOnTable("members", 2)...
// It is possible to chain the calls to navigate to another change.
// Here change at index 7 of "members" table
assertThat(changes).changeOnTable("members", 6).changeOnTable("members")...

There are 12 other methods which are derived from the 4 methods above :

  • changeOfCreation(), changeOfModification() and changeOfDeletion() methods which allows to navigate to the next change of creation, modification and deletion like change() method

// If it is the first call, navigate to the first change of creation
assertThat(changes).changeOfCreation()...
// Navigate to the the first change of creation
// and after the second change of creation
assertThat(changes).changeOfCreation().changeOfCreation()...
  • changeOfCreation(int index), changeOfModification(int index) and changeOfDeletion(int index) methods with index as parameter which allows to navigate to the change of creation, modification and deletion corresponding to change of creation, modification and deletion at the index like change(int index) method

// Navigate to the change of modification at index 2
assertThat(changes).changeOfModification()
                   .changeOfModification(2)...
// It is possible to chain the calls
// to navigate to another change of modification.
// Here change of modification at index 5
assertThat(changes).changeOfModification(4)
                   .changeOfModification()...
  • changeOfCreationOnTable(String tableName), changeOfModificationOnTable(String tableName) and changeOfDeletionOnTable(String tableName) methods with tableName as parameter which allows to navigate to the next change of creation, modification and deletion corresponding to the table name like changeOnTable(String tableName) method

// If it is the first call, navigate
// to the first change of creation on "members" table
assertThat(changes).changeOfCreationOnTable("members")...
// It is possible to chain the calls to navigate
// to the next change of creation on the "members" table
// after the first change of creation on the "members" table
// (so the second change of creation)
assertThat(changes).changeOfCreationOnTable("members")
                   .changeOfCreationOnTable("members")...
  • changeOfCreationOnTable(String tableName, int index), changeOfModificationOnTable(String tableName, int index) and changeOfDeletionOnTable(String tableName, int index) methods with tableName and index as parameters which allows to navigate to the next change of creation, modification and deletion corresponding to the table name and index like changeOnTable(String tableName, int index) method

// Navigate to the change of deletion at index 2 of "members" table
assertThat(changes).changeOfDeletionOnTable("members")
                   .changeOfDeletionOnTable("members", 2)...
// It is possible to chain the calls
// to navigate to another change of deletion.
// Here change of deletion at index 7 of "members" table
assertThat(changes).changeOfDeletionOnTable("members", 6)
                   .changeOfDeletionOnTable("members")...

The changeOnTableWithPks(String tableName, Object…​ pksValues) method allows to navigate to the change corresponding to the table and the primary keys.

// Navigate to the change with primary key 1 of "members" table
assertThat(changes).changeOnTableWithPks("members", 1)...
// It is possible to chain the calls to navigate to the next change
// after the change with primary key 1 of "members" table
assertThat(changes).changeOnTableWithPks("members", 1).change()...

This picture shows from where it is possible to navigate to a change.

db navigation with changes to change

The origin point of the change(…​) methods is the current Changes and the origin point of other methods is the Changes of origin. So if the method is executed from a change, a column, a row or a value it is like if the method was executed from these origins.

That means there is an important difference.

// Navigate to the changes of deletion
// Navigate to the first change of this changes of deletion
assertThat(changes).ofDeletion().change()...
// Navigate to the changes of deletion
// Navigate to the first change of this changes of creation
assertThat(changes).ofDeletion().changeOfCreation()...
// This is equivalent to
assertThat(changes).ofDeletion().ofAll().changeOfCreation()...

When the position is on a change, it is possible to return to the origin.

// Return to the change from a column
assertThat(changes).change().returnToChanges()...

That also means that the two navigations below are equivalent.

// Navigate to the first change
// Return to the changes
// Navigate to the next change
assertThat(changes).change().returnToChanges().change()...
// The same thing is done but the return to the changes is implicit
assertThat(changes).change().change()...
To a Row

These methods are described in the ToRowFromChange interface.

The rowAtStartPoint() and rowAtEndPoint() methods allows to navigate to the row at the start point and at the end point.

// Navigate to the row at the start point
assertThat(changes).change().rowAtStartPoint()...
// Navigate to the row at the end point (note that the methods can be chained)
assertThat(changes).change().rowAtStartPoint().rowAtEndPoint()...

This picture shows from where it is possible to navigate to a row.

db navigation with changes to row

The origin point of the rowAtStartPoint() and rowAtEndPoint() methods is the Change. So if the method is executed from a row, from a column or from a value it is like if the method was executed from the Change.

When the position is on a row, it is possible to return to the origin.

// Return to the change from a row
assertThat(changes).change().rowAtStartPoint().returnToChange()...

That also means that the two navigations below are equivalent.

// Navigate to the first change
// Navigate to the row at start point
// Return to the change from this column
// Navigate to the row at end point
assertThat(changes).change().rowAtStartPoint().returnToChange().rowAtEndPoint()...
// The same thing is done but the return to the change is implicit
assertThat(changes).change().rowAtStartPoint().rowAtEndPoint()...
To a Column

These methods are described in the ToColumn and ToColumnFromChange interfaces.

The column() method allows to navigate to the next column after the column reached on the last call.

// If it is the first call, navigate to the first column
assertThat(changes).change().column()...
// It is possible to chain the calls to navigate to the next column
// after the first column (so the second column)
assertThat(changes).change().column().column()...

The column(int index) method with index as parameter allows to navigate to the column corresponding to column at the index.

// Navigate to the column at index 2
assertThat(changes).change().column(2)...
// It is possible to chain the calls to navigate to another column.
// Here column at index 6
assertThat(changes).change().column(2).column(6)...
// It is possible to combine the calls to navigate to the next column
// after the column at index 2. Here column at index 3
assertThat(changes).change().column(2).column()...
// It is possible to combine the calls with other navigation methods
// Here first column
assertThat(changes).change().rowAtStartPoint().column()...
// Here column at index 3
assertThat(changes).change().rowAtEndPoint().column(3)...
// Here column at index 4 because the origin remember last navigation to a column
assertThat(changes).change().column(3).rowAtEndPoint().column()...

The column(String columnName) method with columnName as parameter allows to navigate to the column corresponding to the column with the column name.

// Navigate to the column with the name "SURNAME"
assertThat(changes).change().column("surname")...
// Like for the other methods, it is possible to chain the calls
assertThat(changes).change().column("surname").column().column(6).column("id")...

The columnAmongTheModifiedOnes() method allows to navigate to the next column with modifications after the column reached on the last call.

// If it is the first call, navigate to the first column with modifications
assertThat(changes).change().columnAmongTheModifiedOnes()...
// It is possible to chain the calls to navigate to the next column
// after the first column (so the second column with modifications)
assertThat(changes).change().columnAmongTheModifiedOnes()
                            .columnAmongTheModifiedOnes()...

The columnAmongTheModifiedOnes(int index) method with index as parameter allows to navigate to the column with modifications corresponding to column at the index.

// Navigate to the column at index 2 (the third column with modifications)
assertThat(changes).change().columnAmongTheModifiedOnes(2)...
// It is possible to chain the calls to navigate to another column.
// Here column at index 0 (the first column with modifications)
assertThat(changes).change().columnAmongTheModifiedOnes(2)
                            .columnAmongTheModifiedOnes(0)...

The columnAmongTheModifiedOnes(String columnName) method with columnName as parameter allows to navigate to the column with modifications corresponding to the column with the column name.

// Navigate to the column with modifications and the name "SURNAME"
assertThat(changes).change().columnAmongTheModifiedOnes("surname")...
// Like for the other methods, it is possible to chain the calls
assertThat(changes).change().column("surname").columnAmongTheModifiedOnes()
                            .column(6).columnAmongTheModifiedOnes("id")...

This picture shows from where it is possible to navigate to a column.

db navigation with changes to column

The origin point of the column(…​) methods is the Change. So if the method is executed from a row, from a column or from a value it is like if the method was executed from the Change.

When the position is on a column, it is possible to return to the origin.

// Return to the change from a column
assertThat(changes).change().column().returnToChange()...

That also means that the two navigations below are equivalent.

// Navigate to the first change
// Navigate to the first column
// Return to the change from this column
// Navigate to the next column
assertThat(changes).change().column().returnToChange().column()...
// The same thing is done but the return to the change is implicit
assertThat(changes).change().column().column()...
To a Value

These methods are described in the ToValue, ToValueFromColumn and ToValueFromRow interfaces.

This picture shows from where it is possible to navigate to a value.

The value() method (only available from a row) allows to navigate to the next value after the value reached on the last call.

// If it is the first call, navigate to the first value
assertThat(changes).change().rowAtEndPoint().value()...
// It is possible to chain the calls to navigate to the next value
// after the first value (so the second value)
assertThat(changes).change().rowAtEndPoint().value().value()...

The value(int index) method with index as parameter (only available from a row) allows to navigate to the value corresponding to value at the index.

// Navigate to the value at index 2
assertThat(changes).change().rowAtEndPoint().value(2)...
// It is possible to chain the calls to navigate to another value.
// Here value at index 6
assertThat(changes).change().rowAtEndPoint().value(2).value(6)...
// It is possible to combine the calls to navigate to the next value
// after the value at index 2. Here value at index 3
assertThat(changes).change().rowAtEndPoint().value(2).value()...
// Here value at index 4 because the origin remember last navigation to the row
assertThat(changes).change().rowAtEndPoint().value(3).column(2).rowAtEndPoint().value()...

The value(String columnName) method with columnName as parameter (only available from a row) allows to navigate to the value of the column corresponding to the column with the column name.

// Navigate to the value of the column with the name "SURNAME"
assertThat(changes).change().rowAtEndPoint().value("surname")...
// Like for the other methods, it is possible to chain the calls
assertThat(changes).change().rowAtEndPoint().value("surname").value().value(6).value("id")...

The valueAtStartPoint() and valueAtEndPoint() methods (only available from a column) allows to navigate to the value at the start point and at the end point.

// Navigate to the value at the start point of the row
assertThat(changes).change().column().valueAtStartPoint()...
// Navigate to the value at the end point of the row (note that the methods can be chained)
assertThat(changes).change().column().valueAtStartPoint().valueAtEndPoint()...

This picture shows from where it is possible to navigate to a value.

db navigation with changes to value

The origin point of the value(…​) methods is the Row or the Column. So if the method is executed from a value it is like if the method was executed from the Row or The Column.

When the position is on a value, it is possible to return to the origin.

// Return to the column from a value
assertThat(changes).change().column().valueAtEndPoint().returnToColumn()...
// Return to the row from a value
assertThat(changes).change().rowAtEndPoint().value().returnToRow()...

That also means that the two navigations below are equivalent.

// Navigate to the first change
// Navigate to the row at end point
// Navigate to the first value
// Return to the column from this value
// Navigate to the next value
assertThat(changes).change().rowAtEndPoint().value().returnToRow().value()...
// The same thing is done but the return to the row is implicit
assertThat(changes).change().rowAtEndPoint().value().value()...

6.3.2. Assertions

On the type of change

These assertions are described in the AssertOnChangeType interface.

These assertions allow to verify the type of a change (the concept of change of type is described here).

// Verify that the first change is a change of creation
assertThat(changes).change().isOfType(ChangeType.CREATION);

There are specific assertion methods for each type of change. For example, the assertion below is equivalent to the one above

assertThat(changes).change().isCreation();
On the equality with the values of a column

These assertions are described in the AssertOnColumnEquality and the AssertOnColumnOfChangeEquality interfaces.

These assertion allow to verify the values of a column (the column of a table, of a request or of a change).

With Boolean
// Verify that the values of the column "live" of the request
// was equal to true, to false and after to true
assertThat(request).column("live").hasValues(true, false, true);
// Verify that the value of the first column of the first change
// was false at start point and is true at end point
assertThat(changes).change().column().hasValues(false, true);
// Verify that the value of the third column of the first change
// is not modified and is true
assertThat(changes).change().column(2).hasValues(true);
With Bytes
// Get bytes from a file and from a resource in the classpath
byte[] bytesFromFile = Assertions.bytesContentOf(file);
byte[] bytesFromClassPath = Assertions.bytesContentFromClassPathOf(resource);
// Verify that the values of the second column of the request
// was equal to the bytes from the file, to null and to bytes from the resource
assertThat(request).column(1).hasValues(bytesFromFile, null, bytesFromClassPath);
// Verify that the value of the first column of the first change
// was equal to bytes from the file at start point and to bytes from the resource at end point
assertThat(changes).change().column().hasValues(bytesFromFile, bytesFromClassPath);
With Number
// Verify that the values of the first column of the table
// was equal to 5.9, 4 and 15000
assertThat(table).column().hasValues(5.9, 4, new BigInteger("15000"));
// Verify that the value of the first column of the first change
// is not modified and is equal to 5
assertThat(changes).change().column().hasValues(5);
With Date
// Verify that the values of the first column of the table
// was equal to December 23rd 2007 and May 19th 1975
assertThat(table).column()
            .hasValues(LocalDate.of(2007, 12, 23),
                       LocalDate.of(1975, 5, 19));
// Verify that the value of the first column of the first change
// was equal December 23rd 2007 at start point
// and is equal to May 19th 1975 at end point
assertThat(changes).change().column()
            .hasValues(LocalDate.parse("2007-12-23"),
                       LocalDate.parse("1975-05-19"));
With Time
// Verify that the values of the first column of the table
// was equal to 09:01am and 05:30:50pm
assertThat(table).column()
            .hasValues(LocalTime.of(9, 1),
                       LocalTime.of(17, 30, 50));
// Verify that the value of the first column of the first change
// was equal to 09:01am at start point
// and is equal to 05:30:50pm at end point
assertThat(changes).change().column()
            .hasValues(LocalTime.parse("09:01"),
                       LocalTime.parse("17:30:50"));
With Date/Time
// Verify that the values of the first column of the table
// was equal to December 23rd 2007 09:01am and May 19th 1975
assertThat(table).column()
            .hasValues(LocalDateTime.of(LocalDate.of(2007, 12, 23),
                                        LocalTime.parse("09:01")),
                       LocalDateTime.of(LocalDate.of(1975, 5, 19),
                                        LocalTime.MIDNIGHT));
// Verify that the value of the first column of the first change
// was equal December 23rd 2007 09:01am at start point
// and is equal to May 19th 1975 at end point
assertThat(changes).change().column()
            .hasValues(LocalDateTime.parse("2007-12-23T09:01"),
                       LocalDateTime.parse("1975-05-19T00:00"));
With String
// Verify that values are equal to texts
assertThat(table).column("name")
            .hasValues("Hewson",
                       "Evans",
                       "Clayton",
                       "Mullen");
// Verify that the value of the column "size" of the first change of modification
// is not modified and is equal to 1.75 by parsing
assertThat(changes).changeOfModification().column("size")
            .hasValues("1.75");
// Verify that values are equal to dates, times or dates/times by parsing
assertThat(table).column()
            .hasValues("2007-12-23T09:01"),
                       "1975-05-19");
With UUID

Since 1.1.0

// Verify that the values of the first column of the table
// was equal to 30B443AE-C0C9-4790-9BEC-CE1380808435, 0E2A1269-EFF0-4233-B87B-B53E8B6F164D
// and 2B0D1BDD-909E-4362-BA10-C930BA82718D
assertThat(table).column().hasValues(UUID.fromString("30B443AE-C0C9-4790-9BEC-CE1380808435"),
                                     UUID.fromString("0E2A1269-EFF0-4233-B87B-B53E8B6F164D"),
                                     UUID.fromString("2B0D1BDD-909E-4362-BA10-C930BA82718D"));
// Verify that the value of the first column of the first change
// is not modified and is equal to 399FFFCA-7874-4225-9903-E227C4E9DCC1
assertThat(changes).change()
                   .column().hasValues(UUID.fromString("399FFFCA-7874-4225-9903-E227C4E9DCC1"));
With Character

Since 1.2.0

// Verify that the values of the first column of the table
// was equal to 'T', 'e', 's' and 't'
assertThat(table).column().hasValues('T', 'e', 's', 't');
// Verify that the value of the first column of the first change
// is not modified and is equal to 'T'
assertThat(changes).change().column().hasValues('T');
On the name of a column

This assertion is described in the AssertOnColumnName interface.

This assertion allows to verify the name of a column (the column of a table, of a request or of a change).

// Verify that the fifth column of the table is called "firstname"
assertThat(table).column(4).hasColumnName("firstname");
// Verify that the third value of the second row of the request is in a column called "name"
assertThat(request).row(1).value(2).hasColumnName("name");
// Verify that the first column of the first change is called "id"
assertThat(changes).change().column().hasColumnName("id");
On the nullity of the values of a column

These assertions are described in the AssertOnColumnNullity interface.

These assertion allows to verify the nullity of the values of a column (the column of a table or of a request).

// Verify that the fifth column of the table has only null values
assertThat(table).column(4).hasOnlyNullValues();
// Verify that the column "name" has only not null values
assertThat(request).column("name").hasOnlyNotNullValues();
On the nullity of the values of a row

Since 1.2.0

These assertions are described in the AssertOnRowNullity interface.

These assertion allows to verify the nullity of the values of a row (the row of a table or of a request).

// Verify that the fifth row of the table has only not null values
assertThat(table).row(4).hasOnlyNotNullValues();
// Verify that the first column has only not null values
assertThat(request).row().hasOnlyNotNullValues();
On the type of column

These assertions are described in the AssertOnColumnType interface.

These assertions allow to verify the type of the values of a column (a column from a table, from a request or from a change).

// Verify that the values of the column called "firstname"
// of the table are a text (null values are considered as wrong)
assertThat(table).column("firstname").isOfType(ValueType.TEXT, false);
// The same verification (with the specific method)
// on the third column of the request
assertThat(request).column(2).isText(false);
// Now the same verification again but with a lenience with null values
// (the null values are not considered as wrong)
assertThat(request).column(2).isText(true);
// Verify that the values of the first column
// of the first change is either a date or a number
assertThat(changes).change().column()
    .isOfAnyOfTypes(ValueType.DATE, ValueType.NUMBER);
On the class of column

Since 1.1.0

This assertion is described in the AssertOnColumnClass interface.

This assertion allows to verify the class of the values of a column (a column from a table, from a request or from a change).

// Verify that the values of the column called "firstname"
// of the table are a String (null values are considered as wrong)
assertThat(table).column("firstname").isOfClass(String.class, false);
// Verify that the values of the first column
// of the first change is a Locale (null values are considered as right)
assertThat(changes).change().column().isOfClass(Locale.class, true);
On the content of column

Since 1.1.0

These assertions are described in the AssertOnColumnContent interface.

These assertions allow to verify the content of a column (a column from a table or from a request).

// Verify that the content of the column called "name"
assertThat(table).column("name").containsValues("Hewson",
                                                "Evans",
                                                "Clayton",
                                                "Mullen");
// This second assertion is equivalent because the order of the values is not important
assertThat(table).column("name").containsValues("Evans",
                                                "Clayton",
                                                "Hewson",
                                                "Mullen");
On the type of data

These assertions are described in the AssertOnDataType interface.

These assertions allow to verify the type of the date on which is a change.

// Verify that the change is on a table
assertThat(changes).change().isOnDataType(DataType.TABLE);
// The same verification (with the specific method)
assertThat(changes).change().isOnTable();
// Verify that the change is on the "members" table
assertThat(changes).change().isOnTable("members");
On the modified columns in a change

These assertions are described in the AssertOnModifiedColumn and the AssertOnModifiedColumns interfaces.

These assertions allow to verify if a column of a change have been modified between the start point and the end point (see the concept of changes).

// Verify that first column of the change is not modified
// and the second column is modified
assertThat(changes).change().column().isNotModified().column().isModified();
// Verify that there are 2 modified columns in the change
assertThat(changes).change().hasNumberOfModifiedColumns(2);
// Verify that the modified column in change are at index 1 and 2
assertThat(changes).change().hasModifiedColumns(1, 2);
// Verify that the modified column in change are "name" and "firstname"
assertThat(changes).change().hasModifiedColumns("name", "firstname");

Since version 1.1.0, there are new assertions which allow to compare the number of modified columns between the start point and the end point.

// Verify that the number of modified columns in the first change is more than 5
assertThat(changes).change().hasNumberOfModifiedColumnsGreaterThan(5);
// Verify that the number of modified columns in the first change is at least 5
assertThat(changes).change().hasNumberOfModifiedColumnsGreaterThanOrEqualTo(5);
// Verify that the number of modified columns in the first change is less than 6
assertThat(changes).change().hasNumberOfModifiedColumnsLessThan(6);
// Verify that the number of modified columns in the first change is at most 6
assertThat(changes).change().hasNumberOfModifiedColumnsLessThanOrEqualTo(6);
On the number of changes

This assertion is described in the AssertOnNumberOfChanges interface.

This assertion allows to verify the number of changes.

// Verify that there are 4 changes
assertThat(changes).hasNumberOfChanges(4);

Since version 1.1.0, there are new assertions which allow to compare the number of changes between the start point and the end point.

// Verify that the number of changes is more than 5
assertThat(changes).hasNumberOfChangesGreaterThan(5);
// Verify that the number of changes is at least 5
assertThat(changes).hasNumberOfChangesGreaterThanOrEqualTo(5);
// Verify that the number of changes is less than 6
assertThat(changes).hasNumberOfChangesLessThan(6);
// Verify that the number of changes is at most 6
assertThat(changes).hasNumberOfChangesLessThanOrEqualTo(6);
On the number of columns

This assertion is described in the AssertOnNumberOfColumns interface.

This assertion allows to verify the number of columns (columns from a table, from a request or from a change).

// Verify that there are 6 columns in the table
assertThat(table).hasNumberOfColumns(6);
// Verify that there are 4 columns in the change
assertThat(changes).change().hasNumberOfColumns(4);

Since version 1.1.0, there are new assertions which allow to compare the number of columns.

// Verify that the number of columns is more than 5
assertThat(table).hasNumberOfColumnsGreaterThan(5);
// Verify that the number of columns is at least 5
assertThat(request).hasNumberOfColumnsGreaterThanOrEqualTo(5);
// Verify that the number of columns is less than 6
assertThat(changes).hasNumberOfColumnsLessThan(6);
// Verify that the number of columns is at most 6
assertThat(changes).hasNumberOfColumnsLessThanOrEqualTo(6);
On the number of rows

This assertion is described in the AssertOnNumberOfRows interface.

This assertion allows to verify the number of rows (rows from a table or from a request).

// Verify that there are 7 rows in the table
assertThat(table).hasNumberOfRows(7);

Since version 1.1.0, there are new assertions which allow to compare the number of rows.

// Verify that the number of rows is more than 5
assertThat(table).hasNumberOfRowsGreaterThan(5);
// Verify that the number of rows is at least 5
assertThat(request).hasNumberOfRowsGreaterThanOrEqualTo(5);
// Verify that the number of rows is less than 6
assertThat(changes).hasNumberOfRowsLessThan(6);
// Verify that the number of rows is at most 6
assertThat(changes).hasNumberOfRowsLessThanOrEqualTo(6);

Since version 1.2.0, there is a new assertion which allow to verify if rows are empty (equivalent to hasNumberOfRows(0)).

// Verify that the table are empty
assertThat(table).isEmpty();
On the primary keys

These assertions are described in the AssertOnPrimaryKey interface.

These assertions allow to verify the names and the values of the columns which compose the primary keys of the rows from a change.

// Verify that the columns of the primary keys are "id" and "name"
assertThat(changes).change().hasPksNames("id", "name");
// Verify that the values of the primary keys are 1 and "HEWSON"
assertThat(changes).change().hasPksValues(1, "HEWSON");
On the equality with the values of a row

This assertion is described in the AssertOnRowEquality interface.

This assertion allow to verify the values of a row (the row of a table, of a request or of a change).

// Verify the values of the row at index 1
assertThat(table).row(1)
                 .hasValues(2,
                            "Evans",
                            "David Howell",
                            "The Edge",
                            DateValue.of(1961, 8, 8),
                            1.77);
// Verify the values of the row at end point
assertThat(changes).change().rowAtEndPoint()
                            .hasValues(5,
                                       "McGuiness",
                                       "Paul",
                                       null,
                                       "1951-06-17",
                                       null);
On the existence of a row in a change

These assertions are described in the AssertOnRowOfChangeExistence interface.

These assertions allow to verify that the row at start point or at end point of a change exists or not (for a creation, the row do not exist at start point and for a deletion it is the contrary : the row do not exist at end point).

// Verify that row at start point exists
assertThat(changes).change().rowAtStartPoint().exists();
// Verify that the row at end point do not exist
assertThat(changes).change().rowAtEndPoint().doesNotExist();
On the chronology of a value

These assertions are described in the AssertOnValueChronology interface.

These assertions allow to compare a value (the value of a table, of a request or of a change) to a date, a time or a date/time.

// Compare the value with a date
assertThat(table).row(1).value("birthdate")
                        .isAfter(DateValue.of(1950, 8, 8));
// Verify the value is between two dates/times
assertThat(changes).change().column("release").valueAtEndPoint()
                            .isAfterOrEqualTo(DateTimeValue.parse("2014-09-08T23:30"))
                            .isBeforeOrEqualTo(DateTimeValue.parse("2014-09-09T05:30"));
On the comparison with a value

These assertions are described in the AssertOnValueComparison interface.

These assertions allow to compare a value (the value of a table, of a request or of a change) to a number.

// Compare the value with a number
assertThat(table).row(1).value("size")
                        .isGreaterThan(1.5);
// Verify the value is between two numbers
assertThat(changes).change().column("size").valueAtEndPoint()
                            .isGreaterThanOrEqualTo(1.7)
                            .isLessThanOrEqualTo(1.8);
On the closeness of a value

Since 1.1.0

These assertions are described in the AssertOnValueCloseness interface.

These assertions allow to verify if a value (the value of a table, of a request or of a change) is close to another.

// Verify if the value is close to 2 with a tolerance of 0.5
// So the values between 1.5 and 2.5 are right
assertThat(table).row(1).value("size")
                        .isCloseTo(2, 0.5);
// Verify the value is close to 05-10-1960 with a tolerance of two days
assertThat(changes).change().column("birth").valueAtEndPoint()
                            .isCloseTo(DateValue(1960, 5, 10),
                                       DateValue(0, 0, 2));
On the equality with a value

These assertions are described in the AssertOnValueEquality interface.

These assertion allow to verify that a value (the value of a table, of a request or of a change) is equal to another value (in parameter).

With Boolean
// Verify that the value is equal to true
assertThat(table).row(3).value("live").isEqualTo(true);
// Do the same thing with the specific method
assertThat(table).row(3).value("live").isTrue();
With Bytes
// Get bytes from a file
byte[] bytesFromFile = Assertions.bytesContentOf(file);
// Verify that the value at end point of the first column of the first change
// is equal to bytes from the file
assertThat(changes).change().column().valueAtStartPoint().isEqualTo(bytesFromFile);
With Number
// Verify that the first value is equal to 1.77,
// the second is equal to 50 and the last is equal to zero
assertThat(request).column("size").value().isEqualTo(1.77)
                                  .value().isEqualTo(50)
                                  .value().isEqualTo(0).isZero();
With Date
// Verify that values are equal to dates
assertThat(changes).changeOfCreation()
                       .rowAtEndPoint()
                           .value("birthdate")
                               .isEqualTo(LocalDate.of(1951, 6, 17))
                   .changeOfModification()
                       .column("birthdate")
                           .isEqualTo()
                               .isNotEqualTo(LocalDate.parse("1960-05-10"))
                           .valueAtEndPoint()
                               .isEqualTo(LocalDate.of(1960, 5, 10));
With Time
// Verify that the value is equal to a time
assertThat(table).row().value("duration").isEqualTo(LocalTime.of(9, 1));
With Date/Time
// Verify that the value is equal to a date/time
assertThat(request).column().value()
                   .isEqualTo(LocalDateTime.of(2007, 12, 23,9, 1, 0))
                   .isEqualTo(LocalDateTime.parse("2007-12-23T09:01"));
With String
// Verify that the values are equal to numbers, texts and dates
assertThat(table).row().value().isEqualTo("1")
                       .value().isEqualTo("Hewson")
                       .value().isEqualTo("Paul David")
                       .value().isEqualTo("Bono")
                       .value().isEqualTo("1960-05-10")
                       .value().isEqualTo("1.75");
With UUID

Since 1.1.0

// Verify that the values are equal to UUID
assertThat(table).column().value().isEqualTo(UUID.fromString("30B443AE-C0C9-4790-9BEC-CE1380808435"))
                          .value().isEqualTo(UUID.fromString("0E2A1269-EFF0-4233-B87B-B53E8B6F164D"))
                          .value().isEqualTo(UUID.fromString("2B0D1BDD-909E-4362-BA10-C930BA82718D"));
With Character

Since 1.2.0

// Verify that the values are equal to Character
assertThat(table).column().value().isEqualTo('T')
                          .value().isEqualTo('e')
                          .value().isEqualTo('s')
                          .value().isEqualTo('t');
On the non equality with a value

These assertions are described in the AssertOnValueNonEquality interface.

These assertion allow to verify that a value (the value of a table, of a request or of a change) is not equal to another value (in parameter).

With Boolean
// Verify that the values (values "live" in the row at index 3 and index 5)
// are not equal to false
assertThat(table).row(3).value("live").isNotEqualTo(false)
                 .row(5).value("live").isNotEqualTo(false);
With Bytes
// Get bytes from a resource in the classpath
byte[] bytesFromClassPath = Assertions.bytesContentFromClassPathOf(resource);
// Verify that the value at end point of the first column of the first change
// is not equal to bytes from the resource
assertThat(changes).change().column().valueAtStartPoint().isNotEqualTo(bytesFromClassPath);
With Number
// Verify that the first value is not equal to 1.78,
// the second is not equal to 55 and the last is not equal to 15
assertThat(request).column("size").value().isNotEqualTo(1.78)
                                  .value().isNotEqualTo(55)
                                  .value().isNotEqualTo(15);
With Date
// Verify that values are not equal to dates
assertThat(changes).changeOfCreation()
                       .rowAtEndPoint()
                           .value("birthdate")
                               .isNotEqualTo(LocalDate.of(1951, 6, 17))
                   .changeOfModification()
                       .column("birthdate")
                           .valueAtStartPoint()
                               .isNotEqualTo(LocalDate.parse("1960-05-10"))
                           .valueAtEndPoint()
                               .isNotEqualTo(LocalDate.of(1960, 5, 10));
With Time
// Verify that the value is equal to a time
assertThat(table).row().value("duration").isNotEqualTo(LocalTime.of(9, 1));
With Date/Time
// Verify that the value is not equal to a date/time
assertThat(request).column().value()
                   .isNotEqualTo(LocalDateTime.of(2015, 5, 26,22, 46)))
                   .isNotEqualTo(LocalDateTime.parse("2015-05-26T22:46"));
With String
// Verify that the values are not equal to numbers, texts and dates
assertThat(table).row().value().isNotEqualTo("5")
                       .value().isNotEqualTo("McGuiness")
                       .value().isNotEqualTo("Paul")
                       .value("birthdate").isNotEqualTo("1951-06-17");
With UUID

Since 1.1.0

// Verify that the values are not equal to UUID
assertThat(table).column()
                 .value().isNotEqualTo(UUID.fromString("30B443AE-C0C9-4790-9BEC-CE1380808435"))
                 .value().isNotEqualTo(UUID.fromString("0E2A1269-EFF0-4233-B87B-B53E8B6F164D"))
                 .value().isNotEqualTo(UUID.fromString("2B0D1BDD-909E-4362-BA10-C930BA82718D"));
With Character

Since 1.2.0

// Verify that the values are not equal to Character
assertThat(table).column()
                 .value().isNotEqualTo('T')
                 .value().isNotEqualTo('e')
                 .value().isNotEqualTo('s')
                 .value().isNotEqualTo('t');
On the nullity of a value

These assertions are described in the AssertOnValueNullity interface.

These assertions allow to verify if a value (the value of a table, of a request or of a change) is null or not.

// Verify that the value at index 1 is null and the next value is not null
assertThat(table).column().value(1).isNull()
                          .value().isNotNull();
// Verify the value is not null
assertThat(changes).change().rowAtStartPoint().value("live")
                            .isNotNull();
On the type of a value

These assertions are described in the AssertOnValueType interface.

This assertion allows to verify the type of a value (a value from a table, from a request or from changes).

// Verify that the value of the column called "firstname"
// of the fifth row of the table is a text
assertThat(table).row(4).value("firstname").isOfType(ValueType.TEXT);
// The same verification (with the specific method)
// on the third value of the second row of the request
assertThat(request).row(1).value(2).isText();
// Verify that the value at start point of the first column
// of the first change is either a date or a number
assertThat(changes).change().column().valueAtStartPoint()
    .isOfAnyOfTypes(ValueType.DATE, ValueType.NUMBER);
On the class of a value

Since 1.1.0

This assertion is described in the AssertOnValueClass interface.

This assertion allows to verify the class of a value (a value from a table, from a request or from changes).

// Verify that the value of the column called "firstname"
// of the fifth row of the table is a String
assertThat(table).row(4).value("firstname").isOfClass(String.class);
// Verify that the value at start point of the first column
// of the first change is a Locale
assertThat(changes).change().column().valueAtStartPoint()
    .isOfClass(Locale.class);

6.4. Javadoc

Latest javadoc release : AssertJ DB javadoc.

6.5. Examples

Some implementations examples are visible at the assertj-db-examples tests. They show what is possible with AssertJ DB Assertions with running code. You can clone the repository and run its tests !

6.6. Mailing list

If you have any questions, please use the AssertJ google group.

6.7. Code and issue tracker

AssertJ-DB is hosted on github : https://github.com/assertj/assertj-db.

Please report bugs or missing features in the AssertJ-DB issue tracker.

6.8. Contributing

Thanks for your interest ! Please check our contributor’s guidelines.

6.9. Release Notes

Latest release notes:

6.9.1. AssertJ DB 2.0.0

Release date : 2020-07-06

Contributors

Thanks to Yosuke Nishikawa ( @sciencesakura ), Pascal Schumacher and @sullis for their contributions.

Improvements

  • Baseline upgrade to Java 8

  • Upgrade to AssertJ Core 3.16.1

  • Support JSR-310 types ( LocalDate, LocalTime, LocalDateTime )

  • Move from CGLIB to Bytebuddy for soft assertions

Breaking changes

  • Baseline upgrade to Java 8

  • Upgrade to AssertJ Core 3.16.1

Chore

  • Replace Travis by GitHub actions

  • Activate SonarCloud analysis

6.9.2. AssertJ DB 1.3.0

Release date : 2019-12-26

Improvements

Fixed

  • issue #62: Fix close-to when tolerance exceed 60 seconds

6.9.3. AssertJ DB 1.2.0

Release date : 2017-05-08

Improvements

  • issue #36: Add order on table (with Order)

  • issue #41: Add BDDAssertions so you can use then() instead of assertThat() (Pascal Schumacher)

  • issue #43: Add isEqualTo(Character) and isNotEqualTo(Character) for values. Add hasValues(Character…​) and containsValues(Character…​) for columns. Add hasValues(Character) and hasValues(Character, Character) for columns of changes

  • issue #45: Add support for testing that a row contains no Null values : hasOnlyNotNullValues() assertion method (fiery-phoenix)

  • Add isEmpty() assertion method related to number of rows (fiery-phoenix)

Fixed

  • issue #39: Add start delimiter and end delimiter for table name and column name (used to generate the request) in Table

  • issue #49: NullPointerException while asserting no changes

  • issue #52: Fix about the outputs with column name and value with %

6.9.4. AssertJ DB 1.1.1

Release date : 2016-04-23

Fixed

  • issue #37: assertj-db 1.1.0 does not work with java 7

6.9.5. AssertJ DB 1.1.0

Release date : 2016-04-14

Improvements

  • Add from(Calendar) and now() methods to DateValue, TimeValue and DateTimeValue

  • issue #9: Provide a way to view the data of a Table, of a Request or of Changes with Outputs

  • issue #15: Add support for UUID type columns (Otoniel Isidoro)

  • Add a isOfClass(Class) assertion method for value and column

  • issue #18: Add isCloseTo(…​) methods for Number, DateValue, TimeValue and DateTimeValue

  • issue #19: Add support for BLOBs and CLOBs

  • issue #22: Add isEqualTo(Object) for values. Add hasValues(Object…​) for columns. Add hasValues(Object) and hasValues(Object, Object) for columns of changes

  • issue #25: Add containsValues(…​) methods for columns

  • issue #29: Add hasNumberOfXXXGreaterThan(…​), hasNumberOfXXXLessThan(…​), hasNumberOfXXXGreaterThanOrEqualTo(…​) and hasNumberOfXXXLessThanOrEqualTo(…​) methods for the rows, columns, changes and modified columns

  • issue #34: Enhance exception message when column does not exist

Fixed

  • issue #21: Add possibility to pass a reference containing a null value like parameter to isEqualTo() and isNotEqualTo()

  • issue #23: Fix support of Numbers (bug when the mapping is a Double instance)

  • issue #31: Fix detection of primary keys (caused by letter case) in some DBMS

  • issue #32: Fix SQL requests for DBMS with letter case different from upper case in the name of the DB elements

6.9.6. AssertJ DB 1.0.1

Release date : 2015-08-09

Fixed

  • issue #13: AbstractMethodError when creating a Table using a Datasource instead of a Source

6.9.7. AssertJ DB 1.0.0

Release date : 2015-07-12

First AssertJ DB release.

7. AssertJ Swing